我正在尝试实现一个目录树视图,它也显示了我的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(对不起,图片上传无效)
答案 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
添加了两个属性:
Name
,一个readonly属性,只返回DirectoryPath
的名称段。如果DirectoryPath
可能在运行时更改,则其setter应调用OnPropertyChanged("Name");
,以便查看Name
属性的绑定将知道他们需要获取新值。 UserFile
获得了类似的Name
属性,如果有可能,会提出有关提升PropertyChanged
的相同建议。
Items
:同样是一个只读属性,如果任何一个成分集合发生变化(句柄PropertyChanged
),你应该适当地提出ICollectionChanged.CollectionChanged
,并且如果在setter中也这样做你有制定者)。绑定不关心属性的声明类型,因此它只返回System.Collections.IEnumerable
- 它甚至可以返回object
,而XAML也不关心。但是,让我们足够具体,而不是特别鼓励C#中的任何人尝试使用该属性。
如果是我,我几乎肯定会让UserDirectory
和UserFile
完全不可变的“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的工作原理是“鸭子打字”。
这是一个更简单的变体也可以使用,因为UserDirectory
和UserFile
都有Name
属性,UserFile
缺少Items
属性是静默地耸了耸肩。
<TreeView
ItemsSource="{Binding RootDirectoryItems}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Items}">
<Label Content="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>