如何在WPF中显示窗口列表

本文关键字:显示 窗口 列表 WPF | 更新日期: 2023-09-27 18:30:14

我正在尝试编写一个程序,其中一部分将显示打开窗口的列表(或者更具体地说,它们的名称或标题)

因此,视图的XAML如下所示:

<Window.DataContext>
  <local:ViewModel />
</Window.DataContext>
<ItemsControl ItemsSource="{Binding Windows}" />

并且ViewModel类看起来像这样:

public ObservableCollection<Window> Windows { get; set; }
public ViewModel()
{
  this.Windows = new ObservableCollection<Window>();
  this.Windows.Add(new Window());
}

这导致程序(和设计器视图)抛出InvalidOperationException: Window must be the root of the tree. Cannot add Window as a child of Visual.

问题似乎是ItemsControl认为我实际上想将Window本身添加为控件,而不是类(我希望窗口显示文本System.Windows.Window或类似内容)。

我试过添加<ItemsControl.ItemTemplate><DataTemplate>...,但结果似乎是一样的。

最后,我尝试创建一个具有单个公共属性Window的伪WindowHolder类。这似乎有效,但似乎是一种非常不雅的做事方式,感觉应该更简单。

tl;dr

问题是"如何在视图模型中绑定到ObservableCollection<Window>的WPF ItemsControl上简单地(最好是在XAML中)显示窗口标题列表?

如何在WPF中显示窗口列表

您可以使用ListBox而不是ItemsControl

<ListBox ItemsSource="{Binding Windows}">
    <ListBox.Resources>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
    </ListBox.Resources>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Title}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

问题是,将控件添加到通用ItemsControl的默认行为导致ContentPresenter被封装在控件本身中。因此,您的集合Windows将希望作为容器添加到ItemsControlItemsPresenter中,但由于异常中描述的原因,它失败了。ListBox之所以有效,是因为包装容器是ListBoxItems,而不是Windows。为了在ItemsControl中为Window提供支持,您必须为ItemsControl实现自己的自定义控件,以返回非Window的容器。

我怀疑你已经找到了最好的方法——听起来WPF不喜欢用对视觉树上已经存在的元素的引用来填充ItemsControl

可视化树必须检查引用,因为我已经通过创建一个包装类来测试了这一点,该包装类具有对Window的引用,然后使用ItemsControl来显示该窗口的内容(看看我是否可以使其爆炸!),它给出了以下错误:

Logical tree depth exceeded while traversing the tree. This could indicate a cycle in the tree.

这是一个无限递归树

总之,最简单的解决方案似乎是为您想要的属性创建一个包装器(您已经完成了),并避免在叶/子节点中引用祖先元素!

您的想法是正确的,您需要为ItemsControl创建一个ItemTemplate。ItemsControl中的每个项都将是Window类型,并且您需要在模板中公开Title属性

<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text={Binding Title}/>
</DataTemplate>
</ItemsControl.ItemTemplate>

我要做的是:

  • 为具有Title属性的窗口ie IWindow创建一个接口包装器(如前所述)
  • 所有Windows都将实现IWindow
  • 制作IWindow的Observable集合,而不是Window