WPF Treeview和数据绑定目录树

时间:2017-10-09 16:57:41

标签: c# wpf xaml mvvm treeview

我正在尝试实现一个目录树视图,它也显示了我的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;
}

但我想重用我的类结构,因为我需要显示Category的{​​{1}}数据,例如。

问题

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

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

1 个答案:

答案 0 :(得分:2)

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被认为是viewmodel的属性,如下所示:

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

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

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

  2. Items:同样是一个只读属性,如果任何一个成分集合发生变化(句柄PropertyChanged),你应该适当地提出ICollectionChanged.CollectionChanged,并且如果在setter中也这样做你有制定者)。绑定不关心属性的声明类型,因此它只返回System.Collections.IEnumerable - 它甚至可以返回object,而XAML也不关心。但是,让我们足够具体,而不是特别鼓励C#中的任何人尝试使用该属性。

  3. 如果是我,我几乎肯定会让UserDirectoryUserFile完全不可变的“POCO”类没有INotifyPropertyChanged,只需重新填充磁盘上是否有任何变化。如果我有理由期望目录会发生很大变化,我可能会通过给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>