上一回的 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 已經幫我們想到非常多的問題與解決方式。在解決問題的時候,千萬不要一味的自己發明輪子,最後又難以維護。
程式碼下載
沒有留言:
張貼留言