WPF TreeView - 如何绑定父/子类

时间:2013-12-17 19:13:46

标签: wpf treeview

我有一个表,我想绑定到TreeView。

NODE    NAME        PARENT
----    -----       -------
1       Bill        NULL
2       Jane        NULL
3       John        1
4       Mike        2
5       Bob         4
6       Jody        1
7       Larry       5
8       Heather     2
9       Steve       8

这是我的班级:

public class Node
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int? Parent { get; set; }

    public Node()
    {
    }

    public Node(int id, string name)
    {
        ID = id;
        Name = name;
        Parent = null;
    }

    public Node(int id, string name, int? parent)
    {
        ID = id;
        Name = name;
        Parent = parent;
    }

    public override string ToString()
    {
        return string.Format("[{0}] {1}", ID, Name);
    }

}

所以最终结果看起来像这样:

---[1] Bill
  |-----[3] John
  |-----[4] Jody

---[2] Jane
  |-----[4] Mike
  |    |------[5] Bob
  |          |------[7] Larry
  |-----[8] Heather
       |------[9] Steve

目前我正在使用代码构建此代码。 首先,我发现所有没有父母的物品。然后从代码中将它们添加到TreeView中:

    var TopNodes = Nodes.Where(n => n.Parent == null);

    foreach (Node n in TopNodes)
    {
        TreeViewItem newItem = new TreeViewItem();
        newItem.Tag = n.ID;
        newItem.Header = n.Name;

        newItem.Items.Add("..Loading..");
        tvTest.Items.Add(newItem);
    }

然后当项目展开时,我填充父项,如下所示:

private void tvTest_Expanded(object sender, RoutedEventArgs e)
{
                item.Items.Clear();

        int parentID = (int)item.Tag;

        var children = Nodes.Where(x => x.Parent == parentID);

        foreach (Node n in children)
        {
            TreeViewItem newItem = new TreeViewItem();
            newItem.Tag = n.ID;
            newItem.Header = n.Name;

            newItem.Items.Add("..Loading..");
            item.Items.Add(newItem);
        }

}

如果我可以直接绑定这些数据,我觉得我会有更大的灵活性。特别是一旦我开始在数据上实现CRUD功能。

感谢您提出任何建议。

2 个答案:

答案 0 :(得分:2)

您可以使用HierarchicalDataTemplate实现此目的。

  1. 修改您的Node类,使其包含类型为List<Node>的名为 Children 的属性。这将包含此节点的所有子节点的列表。
  2. 然后在您的viewmodel中公开一个名为 TopNodes 的类型为List<Node>的公共属性,它将返回顶级节点列表。
  3. 最后在xaml视图中,按如下方式定义TreeView:

    <TreeView Name="TreeViewNodes" ItemsSource="{Binding TopNodes}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate DataType="{x:Type local:Node}" ItemsSource="{Binding Children}">
                <TextBlock Text="{Binding Path=Name}" />
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    
  4. 您仍然需要从viewmodel相应地填充节点对象,但这样可以避免直接从viewmodel引用TreeViewItem,因为这不是遵循MVVM模式时的推荐方法。

    我希望这能回答你的问题:)

答案 1 :(得分:0)

我希望以下示例可以帮助您

Xaml前端

           <TreeView Grid.Row="1" Background="Transparent" ItemsSource="{Binding Directories}" Margin="0,10,0,0" Name="FolderListTreeView"
                Height="Auto" HorizontalAlignment="Stretch" Width="300"  local:ControlBehaviour.ControlEvent="TreeView.SelectedItemChanged" >
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:FileSystem}" ItemsSource="{Binding SubDirectories}">
                        <Label Content="{Binding Path= Name}" Name="NodeLabel" />
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>

保存子目录及其子目录的类

public class FileSystem :NotifyChange, IEnumerable
{
    #region Private members
    private ObservableCollection<FileSystem> subDirectoriesField;
    #endregion

    #region Public properties
    /// <summary>
    /// Gets and sets all the Files in the current folder
    /// </summary>
    public ObservableCollection<FileSystem> SubDirectories
    {
        get
        {
            return subDirectoriesField;
        }
        set
        {
            if (subDirectoriesField != value)
            {
                subDirectoriesField = value;
                RaisePropertyChanged("SubDirectories");
            }
        }
    }
    /// <summary>
    /// Gets or sets name of the file system 
    /// </summary>
    public string Name
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets full path of the file system
    /// </summary>
    public string FullPath
    {
        get;
        set;
    }
    /// <summary>
    /// object of parent, null if the current node is root
    /// </summary>
    public FileSystem Parent
    {
        get;
        set;
    }
    public FileSystem(string fullPath, FileSystem parent)
    {
        Name = fullPath != null ? fullPath.Split(new char[] { System.IO.Path.DirectorySeparatorChar },
            StringSplitOptions.RemoveEmptyEntries).Last()
        FullPath = fullPath;
        Parent = parent;
        FileType = type;
        AddSubDirectories(fullPath);
    }

    public IEnumerator GetEnumerator()
    {
        return SubDirectories.GetEnumerator();
    }

    private void AddSubDirectories(string fullPath)
    {
        string[] subDirectories = Directory.GetDirectories(fullPath);
        SubDirectories = new ObservableCollection<FileSystem>();
        foreach (string directory in subDirectories)
        {
            SubDirectories.Add(new FileSystem(directory, this));
        }
    }
}

然后视图模型将如下所示

public class ViewModel:NotifyChange
{
   private ObservableCollection<FileSystem> directories;
   public ObservableCollection<FileSystem> Directories
    {
        get
        {
            return directoriesField;
        }
        set
        {
            directoriesField = value;
            RaisePropertyChanged("Directories");
        }
    }
    public ViewModel()
    {
       //The below code has to be moved to thread for better user expericen since when UI is loaded it might not respond for some time since it is looping through all the drives and it;s directories
       Directories=new  ObservableCollection<FileSystem>();
       Directories.Add(new FileSystem("C:\\", null);
       Directories.Add(new FileSystem("D:\\", null);
       Directories.Add(new FileSystem("E:\\", null);
    }
}

将DataContext设置为ViewModel

相关问题