• [WP8] Binding时,依照DataType选择DataTemplate


    [WP8] Binding时,依照DataType选择DataTemplate

    范例下载

    范例程序代码:点此下载

    问题情景

    在开发WPF、WP8...这类应用程序的时候,透过Binding机制搭配DataTemplate,能让数据类别在经过Binding之后于画面上呈现。例如下列的范例,透过Binding机制搭配DataTemplate,就能在WP8的ListBox控件中,依照DataTemplate的定义,来呈现Car对象集合。

    • 执行结果

      Sample001

    • 程序代码(.CS)

      namespace BindingSample001.Models
      {
          public class Car
          {
              public string Name { get; set; }
          }
      }
      
      namespace BindingSample001
      {
          public partial class MainPage : PhoneApplicationPage
          {
              // Constructors
              public MainPage()
              {
                  // Initialize
                  this.InitializeComponent();
      
                  // Data
                  var carList = new List<Car>();
                  carList.Add(new Car() { Name = "C001" });
                  carList.Add(new Car() { Name = "C002" });
                  carList.Add(new Car() { Name = "C003" });
                  carList.Add(new Car() { Name = "C004" });
                  carList.Add(new Car() { Name = "C005" });
      
                  // Binding
                  this.ListBox001.ItemsSource = carList;
              }
          }
      }
      
    • 程序代码(.XAML)

      <!--Resources-->
      <phone:PhoneApplicationPage.Resources>
      
          <!--Car Template-->
          <DataTemplate x:Key="CarTemplate">
              <StackPanel Background="LightGreen"  Margin="12,6" FlowDirection="LeftToRight">
                  <TextBlock Text="Car" />
                  <CheckBox Content="{Binding Path=Name}" />
              </StackPanel>
          </DataTemplate>
      
      </phone:PhoneApplicationPage.Resources>
      
      
      <!--LayoutRoot-->
      <ListBox x:Name="ListBox001">
      
          <!--ItemTemplate-->
          <ListBox.ItemTemplate>
              <StaticResource ResourceKey="CarTemplate" />
          </ListBox.ItemTemplate>
      
          <!--Style-->
          <ListBox.ItemContainerStyle>
              <Style TargetType="ListBoxItem">
                  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
              </Style>
          </ListBox.ItemContainerStyle>
      
      </ListBox>
      

    而在一些更复杂的开发项目中,画面上不单单只需要呈现一种的数据类别,而是需要呈现数据类别的各种延伸数据类别,并且这些不同种类的延伸数据类别,在经过Binding之后于画面上必须要有不同种类的呈现外观。例如下列的范例,Car类别为基础数据类别,Truck类别、Sedan类别各自为Car类别的延伸类别,这些Truck类别、Sedan类别在经过Binding之后,于画面上Truck类别会有一种呈现外观、而Sedan类别会有另外一种呈现外观。

    本篇文章介绍如何在Binding时,依照数据类别的DataType选择对应的DataTemplate,用以在Binding之后,于画面上依照不同数据类别来呈现不同外观,为自己留个纪录也希望能帮助到有需要的开发人员。

    • 类别结构

      Class001

    • 类别程序代码(.CS)

      public class Car
      {
          public string Name { get; set; }
      }       
      
      public class Truck : Car
      {
          public int MaxLoad { get; set; }
      }
      
      public class Sedan : Car
      {
          public int MaxSpeed { get; set; }
      }
      

    在WPF中,依照DataType选择DataTemplate

    在WPF中,可以定义DataTemplate.DataType这个属性,用来指定DataTemplate所对应的数据类别。当定义这个属性之后,WPF应用程序在Binding时,就可以依照数据对象的DataType选择DataTemplate,并且使用这个DataTemplate的定义来呈现数据对象。例如下列的范例,Truck类别、Sedan类别在经过Binding之后,于画面上Truck类别会有一种呈现外观、而Sedan类别会有另外一种呈现外观。

    • 执行结果

      Sample002

    • 程序代码(.CS)

      namespace BindingSample002
      {
          public partial class MainWindow : Window
          {
              // Constructors
              public MainWindow()
              {
                  // Initialize
                  this.InitializeComponent();
      
                  // Source
                  var carList = new List<Car>();
                  carList.Add(new Truck() { Name = "T001", MaxLoad = 100 });
                  carList.Add(new Sedan() { Name = "S002", MaxSpeed = 200 });
                  carList.Add(new Truck() { Name = "T003", MaxLoad = 300 });
                  carList.Add(new Truck() { Name = "T004", MaxLoad = 400 });
                  carList.Add(new Sedan() { Name = "S005", MaxSpeed = 500 });
      
                  // Binding
                  this.ListBox001.ItemsSource = carList;
              }
          }
      }
      
    • 程序代码(.XAML)

      <!--Resources-->
      <Window.Resources>
      
          <!--Truck Template-->
          <DataTemplate DataType="{x:Type models:Truck}">
              <StackPanel Background="LightBlue"  Margin="12,6" FlowDirection="LeftToRight">
                  <TextBlock Text="Truck" />
                  <CheckBox Content="{Binding Path=Name}" />
                  <CheckBox Content="{Binding Path=MaxLoad}" />
              </StackPanel>
          </DataTemplate>
      
          <!--Sedan Template-->
          <DataTemplate DataType="{x:Type models:Sedan}">
              <StackPanel Background="LightPink" Margin="12,6" FlowDirection="RightToLeft">
                  <TextBlock Text="Sedan" />
                  <TextBlock Text="{Binding Path=Name}" />
                  <TextBlock Text="{Binding Path=MaxSpeed}" />
              </StackPanel>
          </DataTemplate>
      
      </Window.Resources>
      
      
      <!--LayoutRoot-->
      <ListBox x:Name="ListBox001">
      
          <!--Style-->
          <ListBox.ItemContainerStyle>
              <Style TargetType="ListBoxItem">
                  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
              </Style>
          </ListBox.ItemContainerStyle>
      
      </ListBox>
      

    在WP8中,依照DataType选择DataTemplate

    因为一些原因,在WP8中目前没有提供DataTemplate.DataType这个属性,用来指定DataTemplate所对应的数据类别,这也让WP8应用程序,没有办法提供在Binding之后,于画面上依照不同数据类别来呈现不同外观的这个功能。但是山不转路转,开发人员还是可以透过Binding机制、结合自定义的IValueConverter,来提供依照DataType选择DataTemplate这个功能。

    • 程序代码(.XAML)

      Error001

    首先在页面中使用下列的XAML宣告来取代原有的DataTemplate,让DataTemplate的选择机制,改为使用Binding以及自定义的TypeTemplateConverter来提供。

    • 程序代码(.XAML)

      <!--LayoutRoot-->
      <ListBox x:Name="ListBox001">
      
          <!--ItemTemplate-->
          <ListBox.ItemTemplate>
              <DataTemplate>
                  <ContentControl Content="{Binding}" ContentTemplate="{Binding Converter={StaticResource TypeTemplateConverter}}" HorizontalContentAlignment="Stretch" />
              </DataTemplate>
          </ListBox.ItemTemplate>
      
          <!--Style-->
          <ListBox.ItemContainerStyle>
              <Style TargetType="ListBoxItem">
                  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
              </Style>
          </ListBox.ItemContainerStyle>
      
      </ListBox>
      

    接着就是依照下列的程序代码来建立自定义的TypeTemplateConverter。这个自定义的TypeTemplateConverter实作IValueConverter,用来处理Binding的目标数据对象,并且依照目标数据对象的型别来提供DataTemplate。

    这个设计直觉上会认为没有问题,但实际撰写这个Converter的时候会发现,接收目标数据对象、取得目标数据对象型别这些功能实作都没有问题,但是如何取得DataTemplate却是一个问题(范例中[???]部分的程序代码)。在TypeTemplateConverter并没有定义DataTemplate的来源,没有来源就没有办法取得DataTemplate,那当然也就没有办法依照目标数据对象型别来提供DataTemplate。

    • 程序代码(.CS)

      public sealed class TypeTemplateConverter : IValueConverter
      {
          // Methods 
          public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
          {
              // Require
              if (value == null) return null;
      
              // TypeName
              string typeName = value.GetType().ToString();
      
              // DataTemplate
              DataTemplate dataTemplate = [???];
              if (dataTemplate == null) return null;
      
              // Convert
              return dataTemplate;
          }
      
          public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
          {
              throw new NotImplementedException();
          }
      }
      

    这时为了提供DataTemplate的来源,开发人员可以为TypeTemplateConverter类别加入System.Windows.FrameworkElement类别的继承,这样就可以使用FrameworkElement的Resources属性做为DataTemplate的来源。在这之后TypeTemplateConverter就可以使用目标数据对象型别作为索引,取得Resources属性之中的DataTemplate,用以提供Binding机制使用。

    • 程序代码(.CS)

      namespace BindingSample003.Converters
      {
          public sealed class TypeTemplateConverter : System.Windows.FrameworkElement, IValueConverter
          {
              // Methods 
              public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
              {
                  // Require
                  if (value == null) return null;
      
                  // TypeName
                  string typeName = value.GetType().ToString();
      
                  // DataTemplate
                  var dataTemplate = this.Resources[typeName] as DataTemplate;
                  if (dataTemplate == null) return null;
      
                  // Convert
                  return dataTemplate;
              }
      
              public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
              {
                  throw new NotImplementedException();
              }
          }
      }
      

    为TypeTemplateConverter类别加入System.Windows.FrameworkElement类别的继承之后,在XAML定义中就可以使用XAML语法来定义TypeTemplateConverter对象所要提供的DataTemplate。

    • 程序代码(.XAML)

      <!--Resources-->
      <phone:PhoneApplicationPage.Resources>
      
          <!--Converter-->
          <converters:TypeTemplateConverter x:Key="TypeTemplateConverter" >
              <converters:TypeTemplateConverter.Resources>
      
                  <!--Truck Template-->
                  <DataTemplate x:Key="BindingSample003.Models.Truck">
                      <StackPanel Background="LightBlue"  Margin="12,6" FlowDirection="LeftToRight">
                          <TextBlock Text="Truck" />
                          <CheckBox Content="{Binding Path=Name}" />
                          <CheckBox Content="{Binding Path=MaxLoad}" />
                      </StackPanel>
                  </DataTemplate>
      
                  <!--Sedan Template-->
                  <DataTemplate x:Key="BindingSample003.Models.Sedan">
                      <StackPanel Background="LightPink" Margin="12,6" FlowDirection="RightToLeft">
                          <TextBlock Text="Sedan" />
                          <TextBlock Text="{Binding Path=Name}" />
                          <TextBlock Text="{Binding Path=MaxSpeed}" />
                      </StackPanel>
                  </DataTemplate>
      
              </converters:TypeTemplateConverter.Resources>
          </converters:TypeTemplateConverter>
      
      </phone:PhoneApplicationPage.Resources>
      

    完成上列的设计与定义之后,透过Binding机制、结合自定义的TypeTemplateConverter,WP8应用程序在Binding时,就可以依照数据对象的DataType选择DataTemplate,并且使用这个DataTemplate的定义来呈现数据对象。例如下列的范例,Truck类别、Sedan类别在经过Binding之后,于画面上Truck类别会有一种呈现外观、而Sedan类别会有另外一种呈现外观。

    • 执行结果

      Sample003

    • 程序代码(.CS)

      namespace BindingSample003
      {
          public partial class MainPage : PhoneApplicationPage
          {
              // Constructors
              public MainPage()
              {
                  // Initialize
                  this.InitializeComponent();
      
                  // Data
                  var carList = new List<Car>();
                  carList.Add(new Truck() { Name = "T001", MaxLoad = 100 });
                  carList.Add(new Sedan() { Name = "S002", MaxSpeed = 200 });
                  carList.Add(new Truck() { Name = "T003", MaxLoad = 300 });
                  carList.Add(new Truck() { Name = "T004", MaxLoad = 400 });
                  carList.Add(new Sedan() { Name = "S005", MaxSpeed = 500 });
      
                  // Binding
                  this.ListBox001.ItemsSource = carList;
              }
          }
      }
      
    • 程序代码(.XAML)

      <!--Resources-->
      <phone:PhoneApplicationPage.Resources>
      
          <!--Converter-->
          <converters:TypeTemplateConverter x:Key="TypeTemplateConverter" >
              <converters:TypeTemplateConverter.Resources>
      
                  <!--Truck Template-->
                  <DataTemplate x:Key="BindingSample003.Models.Truck">
                      <StackPanel Background="LightBlue"  Margin="12,6" FlowDirection="LeftToRight">
                          <TextBlock Text="Truck" />
                          <CheckBox Content="{Binding Path=Name}" />
                          <CheckBox Content="{Binding Path=MaxLoad}" />
                      </StackPanel>
                  </DataTemplate>
      
                  <!--Sedan Template-->
                  <DataTemplate x:Key="BindingSample003.Models.Sedan">
                      <StackPanel Background="LightPink" Margin="12,6" FlowDirection="RightToLeft">
                          <TextBlock Text="Sedan" />
                          <TextBlock Text="{Binding Path=Name}" />
                          <TextBlock Text="{Binding Path=MaxSpeed}" />
                      </StackPanel>
                  </DataTemplate>
      
              </converters:TypeTemplateConverter.Resources>
          </converters:TypeTemplateConverter>
      
      </phone:PhoneApplicationPage.Resources>
      
      
      <!--LayoutRoot-->
      <ListBox x:Name="ListBox001">
      
          <!--ItemTemplate-->
          <ListBox.ItemTemplate>
              <DataTemplate>
                  <ContentControl Content="{Binding}" ContentTemplate="{Binding Converter={StaticResource TypeTemplateConverter}}" HorizontalContentAlignment="Stretch" />
              </DataTemplate>
          </ListBox.ItemTemplate>
      
          <!--Style-->
          <ListBox.ItemContainerStyle>
              <Style TargetType="ListBoxItem">
                  <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
              </Style>
          </ListBox.ItemContainerStyle>
      
      </ListBox>
      
  • 相关阅读:
    eval解析的函数传参 object array
    whistle证书过期或不信任
    isa hasa的区别、及理解
    JVM工作原理和特点
    Cookie跨域操作
    关闭当前窗口js
    css遮罩层
    js 获得两个数组的重复元素
    主页变灰
    js 格式化日期
  • 原文地址:https://www.cnblogs.com/clark159/p/3556474.html
Copyright © 2020-2023  润新知