如何在Treeview C#WPF中插入子项

And*_*rin 0 c# wpf treeview

我想在之前添加的TreeViewItem中添加子项.像这样的代码的问题:

如何在WPF中的TreeView控件中插入子节点?

或者我尝试使用的许多其他变体,如:

for (int i = 1; i <= dataTreeview.Items.Count; i++)
        {
             TreeViewItem tempTVI = (TreeViewItem)dataTreeview.Items.GetItemAt(i); 
        }
Run Code Online (Sandbox Code Playgroud)

是我得到一个InvalidCastException异常,因为项目(在另一个stackoverflow问题中)或tempTVI是字符串而不是TreeViewItem

我不知道为什么会这样,我的想法也用完了.

如果它有助于我在预览中使用Visual Studio 2015社区.

谢谢您的帮助.

Xav*_*ier 5

WPF中的TreeViewItemsControl的扩展.基本上有两种方法可以使用这些控件,一种使动态更改树易于管理,另一种使完全静态的树很容易设置.

动态树木

该方法TreeView被设计用来是继MVVM设计模式.这是一个简单的例子.

首先,在使用MVVM时,您总是需要某种实现属性更改通知的视图模型的基类.这是最基本的例子:

internal class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要一个表示树中单个节点的数据的类.例如:

internal class Node : ObservableObject
{
    private ObservableCollection<Node> mChildren;

    // Add all of the properties of a node here. In this example,
    // all we have is a name and whether we are expanded.
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                NotifyPropertyChanged();
            }
        }
    }
    private string _name;

    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (_isExpanded != value)
            {
                _isExpanded = value;
                NotifyPropertyChanged();
            }
        }
    }
    private bool _isExpanded;

    // Children are required to use this in a TreeView
    public IList<Node> Children { get { return mChildren; } }

    // Parent is optional. Include if you need to climb the tree
    // from code. Not usually necessary.
    public Node Parent { get; private set; }

    public Node(Node parent = null)
    {
        mChildren = new ObservableCollection<Node>();
        IsExpanded = true;
        Parent = parent;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,为控件创建一个viewmodel,其中包含这些节点的集合.在此示例中,viewmodel用于应用程序的主窗口:

internal class MainWindowVM : ObservableObject
{
    private ObservableCollection<Node> mRootNodes;

    public IEnumerable<Node> RootNodes { get { return mRootNodes; } }

    public MainWindowVM()
    {
        mRootNodes = new ObservableCollection<Node>();

        // Test data for example purposes
        Node root = new Node() { Name = "Root" };
        Node a = new Node(root) { Name = "Node A" };
        root.Children.Add(a);
        Node b = new Node(root) { Name = "Node B" };
        root.Children.Add(b);
        Node c = new Node(b) { Name = "Node C" };
        b.Children.Add(c);
        Node d = new Node(b) { Name = "Node D" };
        b.Children.Add(d);
        Node e = new Node(root) { Name = "Node E" };
        root.Children.Add(e);
        mRootNodes.Add(root);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,创建TreeView实例并将其设置为使用您的数据.在这个例子中,TreeView主应用程序窗口中唯一的东西是:

<Window x:Class="WpfTreeViewExample.MainWindow"
        x:ClassModifier="internal"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfTreeViewExample"
        SizeToContent="WidthAndHeight"
        Title="MainWindow">
    <Window.DataContext>
        <local:MainWindowVM />
    </Window.DataContext>
    <TreeView
        Margin="10"
        ItemsSource="{Binding RootNodes}">
        <ItemsControl.ItemContainerStyle>
            <Style
                TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
                <!-- Could also put IsSelected here if we needed it in our Node class -->
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <HierarchicalDataTemplate
                DataType="{x:Type local:Node}"
                ItemsSource="{Binding Children}">
                <!-- Can build any view we want here to be used for each node -->
                <!-- Simply displaying the name in a text block for this example -->
                <TextBlock Text="{Binding Name}" />
            </HierarchicalDataTemplate>
        </ItemsControl.ItemTemplate>
    </TreeView>
</Window>
Run Code Online (Sandbox Code Playgroud)

完成此设置后,您从现在开始要做的就是操纵viewmodel中的数据,并TreeView自动更新以反映这些更改.这样,您永远不需要直接操作控件.

以下是结果视图:

截图

静态树木

如果整个树都是提前知道的并且永远不会改变,那么您可以将其设置为非常简单:

<TreeView
    x:Name="mTreeView"
    Margin="10">
    <TreeViewItem Header="Root">
        <TreeViewItem Header="Node A" />
        <TreeViewItem Header="Node B">
            <TreeViewItem Header="Node C" />
            <TreeViewItem Header="Node D" />
        </TreeViewItem>
        <TreeViewItem Header="Node E" />
    </TreeViewItem>
</TreeView>
Run Code Online (Sandbox Code Playgroud)

这种方法的问题在于,当您想以编程方式修改树时,它变得难以管理,因为您必须处理TreeView自身.但是,它可以通过代码隐藏来实现.例如,如果我想在名为"New Node"的"Node C"下添加一个新的子节点,我可以这样做:

((TreeViewItem)((TreeViewItem)((TreeViewItem)mTreeView.Items[0]).Items[1]).Items[0]).Items.Add(new TreeViewItem() { Header = "New Node" });
Run Code Online (Sandbox Code Playgroud)

以这种方式工作变得混乱.由于我们在数据中没有树的并行表示,因此我们必须通过控件继续访问并转换它们.

一些其他设置

看看你的问题,看起来你没有遵循这些方法中的任何一种,而是有一个基本上像这样设置的TreeView:

<TreeView>
    <sys:String>Node A</sys:String>
    <sys:String>Node B</sys:String>
</TreeView>
Run Code Online (Sandbox Code Playgroud)

所以,你有TreeView很多字符串.在内部,ItemsControl可以获取任何对象并将其包装在项容器中.TreeView将这些字符串包装在TreeViewItem实例中.但是,项仍然存储为字符串,访问TreeView.Items将返回您添加的字符串.

获取TreeViewItem与a中的任意项关联TreeView实际上相当困难,因为您必须在根级别获取每个项目的容器,然后深入了解每个项目并获取其项目的容器,以此类推,直到通过树直到找到您要找的物品.

您可以在此处找到有关如何查找项容器的示例.请注意,您不能使用虚拟化技术TreeView为了这个可靠地工作.另外,我建议不要这样做,因为你在自己身上做得更难.