我正在尝试查找其Tag
属性设置为特定值的特定TreeViewItem
。以下FindNode
仅适用于第一级项目,或者在展开父TreeViewItem
时在其他级别中。在下面的示例中,如果展开"FFF",则FindNode
按预期工作。我假设ContainerFromItem
返回 null,因为尚未创建项目。有没有办法强制创建所有树视图项?
<TreeView x:Name="__items">
<TreeViewItem Header="AAA"
Tag="{x:Static my:Node.A}" />
<TreeViewItem Header="BBB"
Tag="{x:Static my:Node.B}">
<!-- Items will be added later. -->
</TreeViewItem>
<TreeViewItem Header="CCC"
Tag="{x:Static my:Node.C}" />
<TreeViewItem Header="DDD"
Tag="{x:Static my:Node.D}" />
<TreeViewItem Header="EEE"
Tag="{x:Static my:Node.E}" />
<TreeViewItem Header="FFF"
Tag="{x:Static my:Node.F}">
<TreeViewItem Header="GGG"
Tag="{x:Static my:Node.G}" />
<TreeViewItem Header="HHH"
Tag="{x:Static my:Node.H}" />
</TreeViewItem>
<TreeViewItem Header="III"
Tag="{x:Static my:Node.I}" />
</TreeView>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
TreeViewItem a = FindNode(__items.ItemContainerGenerator, __items.Items, Node.H);
}
private TreeViewItem FindNode(ItemContainerGenerator gen, ItemCollection items, Node value)
{
TreeViewItem oResult = null;
foreach (var oItem in items)
{
TreeViewItem oTreeViewItem = (TreeViewItem)gen.ContainerFromItem(oItem);
if (oTreeViewItem == null) { continue; }
if ((Node)oTreeViewItem.Tag == value) { oResult = oTreeViewItem; break; }
if (oTreeViewItem.Items.Count > 0)
{
oResult = FindNode(oTreeViewItem.ItemContainerGenerator, oTreeViewItem.Items, value);
if (oResult != null) { break; }
}
}
return oResult;
}
}
public enum Node { A, B, C, D, E, F, G, H, I, J, }
根据 hbarck 的回答,正确的FindNode
实现是:
private TreeViewItem FindNode(ItemCollection items, Node value)
{
TreeViewItem oResult = null;
foreach (var oItem in items)
{
TreeViewItem oTreeViewItem = (TreeViewItem)oItem;
if ((Node)oTreeViewItem.Tag == value) { oResult = oTreeViewItem; break; }
if (oTreeViewItem.Items.Count > 0)
{
oResult = FindNode(oTreeViewItem.Items, value);
if (oResult != null) { break; }
}
}
return oResult;
}
您可以在 ItemsControl 中强制创建 Items。访问 ItemContainerGenerator,然后(这是神奇的;))将其转换为 IItemContainerGenerator,因为接口是显式实现的。使用 StartAt 和 GenerateNext 可以强制创建项目。
请参阅: 为什么 ItemContainerGenerator 返回 null?您只需要对所有项目执行此操作。
TreeViewItems 存在的最简单方法可能是在每个项目上设置 IsExpanded="True",并在 TreeView 上将 IsVirtualizing 设置为 False。只是出于好奇:如果您不使用 ItemGenerator,而只是直接迭代 Items 集合,会发生什么?由于你没有使用数据模板,因此我认为 Items 集合应包含 XAML 文件中的硬编码项。
我已经完成了以下测试:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TreeView x:Name="TestTreeView">
<TreeViewItem Header="Item 1">
<TreeViewItem Header="Item 1 1"/>
<TreeViewItem Header="Item 1 2"/>
</TreeViewItem>
<TreeViewItem Header="Item 2"/>
</TreeView>
<Button x:Name="TestButton" Click="TestButton_Click">Test</Button>
</StackPanel>
</Window>
和代码隐藏:
Class MainWindow
Private Sub TestButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
Stop
For Each t As TreeViewItem In Me.TestTreeView.Items
Debug.Print("Item: {0}, Child count:{1}", t.Header, t.Items.Count)
Next
End Sub
End Class
调试窗口中的输出为
Item: Item 1, Child count:2
Item: Item 2, Child count:0
这意味着这些项是实例化的,并且可以迭代,即使在子级别中也是如此。可能进入你的方式是ItemContainerGenerator,实际上只有在使用DataTemplates时才需要它。此外,您可能必须等到窗口的 Loaded 事件之后才能实例化所有项目。