上一回的 WPF TreeView 的 DataBinding (1),TreeView繫結的是單一型別物件的樹狀結構。這一次要挑戰的是同時繫結到兩種型別。
資料
和上次不同的,這一次目錄 (MyFolder)這個容器(container)中,可同時持有MyFolder 與 MyFile。為此,我們修改程式如下
public static List<MyFolder> GetAllFolder() { var folders = new List<MyFolder>() { new MyFolder("A") { Files = new List<MyFile>() { new MyFile("d_1")}, SubFolders = new List<MyFolder>(){ new MyFolder("A1") { Files = new List<MyFile>() { new MyFile("d1_1"), new MyFile("d1_2")}, SubFolders = new List<MyFolder>() { new MyFolder("A11"), new MyFolder("A12"), new MyFolder("A13"), new MyFolder("A14"), } }, new MyFolder("A2"), new MyFolder("A3") } }, new MyFolder("B") { Files = new List<MyFile>() { new MyFile("db_1"), new MyFile("db_2")}, SubFolders = new List<MyFolder>(){ new MyFolder("B1"), new MyFolder("B2"), new MyFolder("B3"), new MyFolder("B4"), } } }; return folders; }
繫結
.NET Framework 的元件(control)在進行繫結時,只能繫結到單一物件或同一類別的集合。因此,TreeView 要同時顯示 MyFolder 或 MyFile 時,ItemsSource 給什麼值呢?是 SubFolders 嗎?還是 Files 嗎?都只能顯示一部份。為了讓 ItemsSource 在繫結時可以繫結到所有的children,我們必須製作一個新的屬性 Items,可讀取到所有的children,包含MyFolder 及 MyFiles。故程式碼修改如下
public abstract class MyItem { public string Name { get; set; } } public class MyFolder : MyItem { public MyFolder(string name) { Name = name; } public List<MyFolder> SubFolders { get; set; } public List<MyFile> Files { get; set; } public List<MyItem> Items { get { var items = new List<MyItem>(); items.AddRange(SubFolders.AsEnumerable()); items.AddRange(Files.AsEnumerable()); return items; } } }
xaml 修改如下
<Window x:Class="WpfTreeView1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfTreeView1="clr-namespace:WpfTreeView1" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <WpfTreeView1:FolderHelper x:Key="helper" /> </Window.Resources> <Grid> <TreeView Name="treeView1" ItemsSource="{Binding Source={StaticResource ResourceKey=helper}, Path=Items}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding Items}"> <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> </Window>
注意到我們ItemsSource改繫結到 Items。程式運作結如果附圖
圖示
上面運作的結果是正確的,但風格還是無法讓使用者接受。原因在於無法分辨何者為目錄,何者為檔案。
但怎麼改呢?HierarchicalDataTemplate 只有一個,如何才能分開顯示目錄及檔案呢?
答案是實作一個 DataTemplateSelector。
DataTemplateSelector 的想法是這樣:資料繫結的過程中,當遇到某類型的物件時,套用指定的樣版。而這樣的邏輯是完全客製化的,因此必須自己實作。在 WPF 中,就是繼承DataTemplateSelector, 實作自己的 SelectTemplate。程式如下
using System.Windows; using System.Windows.Controls; namespace WpfTreeView1 { public class MyDataTemplateSelector : DataTemplateSelector { public HierarchicalDataTemplate FileTemplate { get; set; } public HierarchicalDataTemplate FolderTemplate { get; set; } public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) { if (item is MyFolder) return FolderTemplate; else return FileTemplate; } } }
xaml
<Window x:Class="WpfTreeView1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfTreeView1="clr-namespace:WpfTreeView1" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <WpfTreeView1:FolderHelper x:Key="helper" /> <HierarchicalDataTemplate x:Key="folder" ItemsSource="{Binding Items}"> <StackPanel Orientation="Horizontal"> <Image Source="/WpfTreeView1;component/Images/folder.png" /> <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/> </StackPanel> </HierarchicalDataTemplate> <HierarchicalDataTemplate x:Key="file"> <StackPanel Orientation="Horizontal"> <Image Source="/WpfTreeView1;component/Images/doc.png" /> <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/> </StackPanel> </HierarchicalDataTemplate> <WpfTreeView1:MyDataTemplateSelector x:Key="selector" FolderTemplate="{StaticResource ResourceKey=folder}" FileTemplate="{StaticResource ResourceKey=file}" /> </Window.Resources> <Grid> <TreeView Name="treeView1" ItemsSource="{Binding Source={StaticResource ResourceKey=helper}, Path=Items}" ItemTemplateSelector="{StaticResource ResourceKey=selector}"> </TreeView> </Grid> </Window>程式運行結果如下
結論
WPF 已經幫我們想到非常多的問題與解決方式。在解決問題的時候,千萬不要一味的自己發明輪子,最後又難以維護。
程式碼下載
沒有留言:
張貼留言