C# WPF Treeview和数据绑定目录树

C# WPF Treeview和数据绑定目录树,c#,wpf,xaml,mvvm,treeview,C#,Wpf,Xaml,Mvvm,Treeview,我正在尝试实现一个目录树视图,该视图还显示MVVM项目中的所有文件。模型中的我的文件夹和文件结构如下所示: public class UserDirectory { private ObservableCollection<UserFile> files; private ObservableCollection<UserDirectory> subfolders; private String directoryPath; //public ge

我正在尝试实现一个目录树视图,该视图还显示MVVM项目中的所有文件。
模型中的我的文件夹和文件结构如下所示:

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
}
public ObservableCollection<Object> RootDirectoryItems { get; } 
    = new ObservableCollection<object>();
但是我想重用我的类结构,因为我需要显示
UserFile
Category
数据

问题

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

(很抱歉,图像上传不起作用)

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>
在C#中,假设所有属性设置器上都存在
INotifyPropertyChanged
样板文件。我向
UserDirectory
添加了两个属性:

  • Name
    ,一个只读属性,只返回
    DirectoryPath
    的名称段。如果
    DirectoryPath
    可能在运行时更改,则其设置程序应调用
    OnPropertyChanged(“名称”)
    ,这样查看
    Name
    属性的绑定将知道需要获取新值
    UserFile
    获得了一个类似的
    Name
    属性,如果可能的话,它还附带了关于提升
    PropertyChanged
    的相同建议

  • :同样是一个只读属性,如果其中一个组成集合发生更改,您应该适当地提高
    属性更改
    (处理
    ICollectionChanged.CollectionChanged
    ,如果您有setter,则在setter中也要这样做)。绑定不关心属性的声明类型,因此它只返回
    System.Collections.IEnumerable
    ——它甚至可以返回
    对象
    ,XAML也不关心。但是,让我们足够具体,不要过于具体,以至于鼓励C#中的任何人尝试将该属性用于任何事情

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

    下面是C#:


    谢谢!这是一个非常好的书面解释,我只有一些疑问。1) 我不明白
    RootDirectoryItems
    的目的,我应该在这里确切地输入什么?2) 另外,使用
    子文件夹?.Cast().Union(文件)我们基本上是将
    子文件夹
    文件
    列表合并到
    项目
    列表中,对吗?因为我有同样的想法,但试图用
    CollectionContainer
    将它们放在ViewModel中,但不知道如何前进。1
    RootDirectoryItems
    是用来填充树视图根目录的项
    UserDirectory
    可能还有一些
    UserFile
    。你以前是怎么填充它的?2.是的,这将生成所有子文件夹的结果枚举,后跟所有文件。您不需要任何花哨的东西,只需要这些对象的任何旧序列即可供TreeView枚举。事实上,我以前并没有填充它。我的ViewModel中有一个
    UserDirectory-rootDirectory
    属性,它被赋予
    Worker
    类类型以查找其文件和子文件夹。我可以在
    RootDirectoryItems
    get
    中返回
    RootDirectoryItems
    项目吗?@RiccardoLomazzi。或者使
    RootDirectory
    成为公共属性并绑定
    ItemsSource=“{Binding RootDirectory.Items}”
    。一旦您编写了将目录枚举到
    UserDirectory
    实例中的代码,只需使用
    UserDirectory
    就可以了。非常感谢您的输入。我现在正在努力实现它。很难保持这种POCO结构,我想我只需要在UserFile和UserDirectory的属性上实现一个OnPropertyChanged(是的,我将来需要修改它们的路径)。但是那
    FileWatcher
    引起了我的好奇心。。。
    public ObservableCollection<Object> RootDirectoryItems { get; } 
        = new ObservableCollection<object>();
    
    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); } }
    }
    
        <TreeView
            ItemsSource="{Binding RootDirectoryItems}"
            >
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                    <Label Content="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>