【问题标题】:WPF Treeview and databinding a Directory TreeWPF Treeview 和数据绑定目录树
【发布时间】:2018-03-20 22:59:53
【问题描述】:

我正在尝试实现一个目录树视图,该视图还显示了我的 MVVM 项目中的所有文件。我在Model 中的文件夹和文件结构是这样的:

public class UserDirectory
{
   private ObservableCollection<UserFile> files;
   private ObservableCollection<UserDirectory> subfolders;
   private String directoryPath;
   //public getters and setters...
}

public class UserFile
{
   private String filePath;
   private Category category; //Archive, Document, Exe, etc...
   //public getters and setters
}

我想在 TreeView 中展示它们,但在阅读了 this very helpful Josh Smith article 和其他各种来源之后,我仍然不知道如何使用 HierarchicalDataTemplate 解决它。

可能的解决方案

我发现也许我必须创建一个特定类型,例如 Item,它的存在仅用于显示文件和目录的名称,

public class Item
{
    private List<String> directories;
    private List<String> files;
}

但我想重用我的类结构,因为我需要显示UserFileCategory 数据,例如。

问题

如何在维护当前数据结构的同时显示文件和子文件夹?

This is an example of what I want to reach(对不起,图片上传不起作用)

【问题讨论】:

    标签: c# wpf xaml mvvm treeview


    【解决方案1】:

    XAML

    <TreeView
        ItemsSource="{Binding RootDirectoryItems}"
        >
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:UserDirectory}" ItemsSource="{Binding Items}">
                <Label Content="{Binding Name}" />
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type local:UserFile}">
                <Label Content="{Binding Name}" />
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>
    

    RootDirectoryItems 被假定为视图模型的属性,如下所示:

    public ObservableCollection<Object> RootDirectoryItems { get; } 
        = new ObservableCollection<object>();
    

    在 C# 中,假设所有属性设置器上都存在 INotifyPropertyChanged 样板。我给UserDirectory添加了两个属性:

    1. Name,一个只读属性,它只返回DirectoryPath 的名称段。如果DirectoryPath 可能在运行时改变,它的setter 应该调用OnPropertyChanged("Name");,以便查看Name 属性的绑定知道它们需要获取新值。 UserFile 获得了类似的 Name 属性,如果有可能,它会提供关于提高 PropertyChanged 的相同建议。

    2. Items:同样,一个只读属性,如果任何一个组成集合发生变化,你应该适当地提高PropertyChanged(处理ICollectionChanged.CollectionChanged,如果你有设置器,在设置器中也这样做)。绑定不关心属性的声明类型,所以它只返回System.Collections.IEnumerable——它甚至可以返回object,而XAML 不会关心。但是,让我们足够具体,不要过于具体以鼓励 C# 中的任何人尝试将该属性用于任何事情。

    如果是我,我几乎肯定会在没有 INotifyPropertyChanged 的情况下创建 UserDirectoryUserFile 裸不可变的“POCO”类,并且如果磁盘上有任何更改,只需重新填充即可。如果我有理由期望目录会发生很大变化,我可能会通过给UserDirectory 一个FileWatcher 并让它重新填充自己来脱离不变性。

    所以这里是 C#:

    public class UserDirectory
    {
        public ObservableCollection<UserFile> Files { get; set; } = new ObservableCollection<UserFile>();
        public ObservableCollection<UserDirectory> Subfolders { get; set; } = new ObservableCollection<UserDirectory>();
    
        //  Concat demands a non-null argument
        public IEnumerable Items { get { return Subfolders?.Cast<Object>().Concat(Files); } }
    
        public String DirectoryPath { get; set; }
        public String Name { get { return System.IO.Path.GetFileName(DirectoryPath); } }
    }
    
    public class UserFile
    {
        public String FilePath { get; set; }
        public Category Category { get; set; }
    
        public String Name { get { return System.IO.Path.GetFileName(FilePath); } }
    }
    

    不需要您的 Item 类,因为 XAML 通过“鸭子类型”工作。

    这里有一个更简单的变体也可以工作,因为UserDirectoryUserFile 都有一个Name 属性,而UserFile 缺少的Items 属性被悄悄地解决了。

        <TreeView
            ItemsSource="{Binding RootDirectoryItems}"
            >
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                    <Label Content="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    

    【讨论】:

    • 非常感谢!这是一个很好的书面解释,我只有几个疑问。 1)我不明白RootDirectoryItems的目的,我应该在这里输入什么? 2) 另外,对于Subfolders?.Cast&lt;Object&gt;().Union(Files);,我们基本上是将SubfoldersFiles 列表合并到Items 列表中,对吧?因为我也有同样的想法,但试图用CollectionContainer将它们放在ViewModel中,但不知道如何前进。
    • 1. RootDirectoryItems 是您填充 TreeView 根目录的项目。 UserDirectory 可能还有一些 UserFile。你之前是怎么填充的? 2. 是的,这会产生所有子文件夹的结果枚举,然后是所有文件。你不需要任何花哨的东西——只要这些对象的任何旧序列供 TreeView 枚举。
    • 1.实际上,我之前并没有填充它。我的 ViewModel 中有一个 UserDirectory rootDirectory 属性,该属性被赋予 Worker 类类型以查找其文件和子文件夹。我可以在RootDirectoryItemsget 中返回rootDirectoryItems 吗?
    • @RiccardoLomazzi 绝对。或者将RootDirectory 设为公共属性并绑定ItemsSource="{Binding RootDirectory.Items}"。一旦您编写了将目录枚举到 UserDirectory 实例的代码,只需为此目的使用 UserDirectory 就很简单了。
    • 非常感谢您的意见。我现在正在尝试实施它。很难保持这种 POCO 结构,我想我只会在 UserFile 和 UserDirectory 的属性上实现 OnPropertyChanged(是的,我将来需要修改它们的路径)。但是FileWatcher 有我的好奇心......
    猜你喜欢
    • 2011-09-12
    • 1970-01-01
    • 2022-09-30
    • 2014-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多