WPF DataTemplate Selector (1)

  • 596
  • 0

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>
範例的結果畫面

 

範例參考請點選此連結