WPF 中應用 DataTemplate Selector 的基本形式介紹。
前幾天有朋友問了一個問題:『如果一個集合式的資料儲存的內容可能有多種不同的型別,當使用 ItemsControl 系列的 UI Element 展現資料的時候,能否依據各別的資料型別套用不同的 DataTemplate?』
答案很簡單,ItemsControl 裡有個 ItemTemplateSelector 屬性就是為了目的而存在的。你只要繼承 DataTemplateSelector 並覆寫其 SelectTemplate 方法再指派給 ItemTemplateSelector 屬性就行了。
DataTemplateSelector.SelectTemplate Method
這個方法是這樣宣告的:
public virtual System.Windows.DataTemplate SelectTemplate (object item, System.Windows.DependencyObject container);
(1) 參數 item 代表的是繫結到 UI 上的單一資料物件,簡單來說就是集合中的單一元素物件。
(2) 參數 container 代表的是單一物件展現的容器,通常會是 ContentPresenter。
(3) 至於回傳值,當然就是回傳某一個 DataTemplate。
準備 View Model
這個範例我設定了兩個資料型別 Person 和 Car,畫面繫結的主要資料來源則是 MainViewModel:
public class Person : NotifyPropertyBase
{
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
private int _age;
public int Age
{
get => _age;
set => SetProperty(ref _age, value);
}
}
public class Car : NotifyPropertyBase
{
private string _brand;
public string Brand
{
get => _brand;
set => SetProperty(ref _brand, value);
}
private Color _color;
public Color Color
{
get => _color;
set => SetProperty(ref _color , value);
}
}
public class MainViewModel : NotifyPropertyBase
{
private ObservableCollection<object> _data;
public ObservableCollection<object> Data
{
get => _data;
set => SetProperty(ref _data, value);
}
public MainViewModel ()
{
CreateFakeData();
}
private void CreateFakeData()
{
Data = new ObservableCollection<object>
{
new Person { Name = "John" , Age= 17},
new Person { Name = "Tom" , Age=20},
new Car { Brand = "BMW" , Color = Colors.LightBlue },
new Car { Brand = "Benz" , Color = Colors.LightGray },
new Car { Brand = "Lexus" , Color = Colors.RosyBrown },
new Person { Name = "Alex" , Age= 25},
new Person { Name = "Jeff" , Age=18},
};
}
}
建立 DataTemplateSelector
由於目前還沒有建立 XAML Code,我們先預定兩個 Template 所對應的 Key 值分別為 personTemplate 和 carTemplate。
public class MainDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (container is FrameworkElement element)
{
if (item is Person)
{
return GetTemplate("personTemplate");
}
if (item is Car)
{
return GetTemplate("carTemplate");
}
}
return null;
DataTemplate GetTemplate(string name)
{
return element.FindResource(name) as DataTemplate;
}
}
}
完成 MainWindow 的 XAML
這裡有幾項主要工作要完成:
(1) 指派 Window.DataContext
(2) 在 ResourceDictionary 加入兩個 DataTemplate
(3) 在 ResouceDictionary 加入剛剛完成的 selector
(4) 設定 ListBox 的 ItemTemplateSelector 屬性
<Window x:Class="DataTemplateSelectorSample001.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataTemplateSelectorSample001"
xmlns:vm="clr-namespace:DataTemplateSelectorSample001.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext >
<vm:MainViewModel />
</Window.DataContext>
<Window.Resources >
<local:MainDataTemplateSelector x:Key="selector"/>
<DataTemplate x:Key="personTemplate">
<StackPanel Orientation="Horizontal" Margin="6">
<TextBlock Text="Name : "/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="Age : " Margin="6,0,0,0"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="carTemplate">
<Border Margin="4">
<Border.Background >
<SolidColorBrush Color="{Binding Color}"/>
</Border.Background>
<StackPanel Margin="2">
<TextBlock Text="Brand"/>
<TextBlock Text="{Binding Brand}"/>
</StackPanel>
</Border>
</DataTemplate>
</Window.Resources>
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding Data}" ItemTemplateSelector="{StaticResource selector}"/>
</Window>
範例的結果畫面
範例參考請點選此連結。