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添加了两个属性:
Name,一个只读属性,它只返回DirectoryPath 的名称段。如果DirectoryPath 可能在运行时改变,它的setter 应该调用OnPropertyChanged("Name");,以便查看Name 属性的绑定知道它们需要获取新值。 UserFile 获得了类似的 Name 属性,如果有可能,它会提供关于提高 PropertyChanged 的相同建议。
Items:同样,一个只读属性,如果任何一个组成集合发生变化,你应该适当地提高PropertyChanged(处理ICollectionChanged.CollectionChanged,如果你有设置器,在设置器中也这样做)。绑定不关心属性的声明类型,所以它只返回System.Collections.IEnumerable——它甚至可以返回object,而XAML 不会关心。但是,让我们足够具体,不要过于具体以鼓励 C# 中的任何人尝试将该属性用于任何事情。
如果是我,我几乎肯定会在没有 INotifyPropertyChanged 的情况下创建 UserDirectory 和 UserFile 裸不可变的“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 通过“鸭子类型”工作。
这里有一个更简单的变体也可以工作,因为UserDirectory 和UserFile 都有一个Name 属性,而UserFile 缺少的Items 属性被悄悄地解决了。
<TreeView
ItemsSource="{Binding RootDirectoryItems}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<Label Content="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>