昏睡領域 发表于 2013-1-3 15:37:18

[WPF] DataTemplate Binding to Interface

<div id="cnblogs_post_body">在WPF内可以使用DataTemplate,来辅助完成对对象集合做DataBinding的工作。并且透过DataTemplate的DataType属性,来让对象集合中不同的对象,经过DataBinding之后能有「不同的外观」。简单的范例如下:不同的车辆类型,会依照车辆类型,呈现不同的详细数据。

namespace BindingInterfaceSample{    public class Car    {      public string Name { get; set; }    }    public class Truck : Car    {      public int MaxLoad { get; set; }    }    public class Roadster : Car    {      public int MaxSpeed { get; set; }    }}
<Window x:Class="BindingInterfaceSample.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:clk="clr-namespace:BindingInterfaceSample"      Title="MainWindow" Height="350" Width="525">    <ItemsControl >      <ItemsControl.Items>            <clk:Truck Name="Truck01" MaxLoad="100" />            <clk:Truck Name="Truck02" MaxLoad="500" />            <clk:Roadster Name="Roadster01" MaxSpeed="99" />      </ItemsControl.Items>      <ItemsControl.Resources>            <DataTemplate DataType="{x:Type clk:Truck}">                <WrapPanel>                  <TextBlock Text="Truck" />                  <TextBlock Text="{Binding Path=MaxLoad}" />                </WrapPanel>            </DataTemplate>            <DataTemplate DataType="{x:Type clk:Roadster}">                <WrapPanel>                  <TextBlock Text="Roadster" />                  <TextBlock Text="{Binding Path=MaxSpeed}" />                </WrapPanel>            </DataTemplate>      </ItemsControl.Resources>    </ItemsControl></Window>
http://Files.Dotblogs.com.tw/clark/1205/201252919522525.png

反过来说,也可以经由DataTemplate的DataType属性,来让对象集合中不同的对象,经过DataBinding之后能有「相同的外观」。简单的范例如下:只要是车,都只呈现车的数据。

namespace BindingInterfaceSample{    public class Car    {      public string Name { get; set; }    }    public class Truck : Car    {      public int MaxLoad { get; set; }    }    public class Roadster : Car    {      public int MaxSpeed { get; set; }    }}
<Window x:Class="BindingInterfaceSample.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:clk="clr-namespace:BindingInterfaceSample"      Title="MainWindow" Height="350" Width="525">    <ItemsControl >      <ItemsControl.Items>            <clk:Truck Name="Truck01" MaxLoad="100" />            <clk:Truck Name="Truck02" MaxLoad="500" />            <clk:Roadster Name="Roadster01" MaxSpeed="99" />      </ItemsControl.Items>      <ItemsControl.Resources>            <DataTemplate DataType="{x:Type clk:Car}">                <Button Content="{Binding Path=Name}" />            </DataTemplate>      </ItemsControl.Resources>    </ItemsControl></Window>
http://Files.Dotblogs.com.tw/clark/1205/2012529195332.png

而在让对象集合中不同的对象,经过DataBinding之后能有「相同的外观」。这个使用情景中,DataTemplate的DataType如果设定为Interface型别,在WPF中会出现意外的执行结果。简单的范例如下:DataType设定为Interface型别,会无法对应DataTemplate。

namespace BindingInterfaceSample{    public interface ICar    {      string Name { get; set; }    }    public class Truck : ICar    {      public string Name { get; set; }      public int MaxLoad { get; set; }            }    public class Roadster : ICar    {      public string Name { get; set; }      public int MaxSpeed { get; set; }    }}
<Window x:Class="BindingInterfaceSample.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:clk="clr-namespace:BindingInterfaceSample"      Title="MainWindow" Height="350" Width="525">    <ItemsControl >      <ItemsControl.Items>            <clk:Truck Name="Truck01" MaxLoad="100" />            <clk:Truck Name="Truck02" MaxLoad="500" />            <clk:Roadster Name="Roadster01" MaxSpeed="99" />      </ItemsControl.Items>      <ItemsControl.Resources>            <DataTemplate DataType="{x:Type clk:ICar}">                <Button Content="{Binding Path=Name}" />            </DataTemplate>      </ItemsControl.Resources>    </ItemsControl></Window>
http://Files.Dotblogs.com.tw/clark/1205/2012529195330833.png

遇到必须设定DataTemplate的DataType为Interface型别的情景。解决的方法很简单:只要改写数据系结容器使用的DataTemplateSelector,增加以Interface型别做索引,查询Resource里的DataTemplate,再用这个DataTemplate来做DataBinding。简单的范例如下:只要是有实做车接口,都只呈现车的数据。

namespace BindingInterfaceSample{    public interface ICar    {      string Name { get; set; }    }    public class Truck : ICar    {      public string Name { get; set; }      public int MaxLoad { get; set; }    }    public class Roadster : ICar    {      public string Name { get; set; }      public int MaxSpeed { get; set; }    }}
<Window x:Class="BindingInterfaceSample.MainWindow"      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:clk="clr-namespace:BindingInterfaceSample"      Title="MainWindow" Height="350" Width="525">    <ItemsControl>      <ItemsControl.Items>            <clk:Truck Name="Truck01" MaxLoad="100" />            <clk:Truck Name="Truck02" MaxLoad="500" />            <clk:Roadster Name="Roadster01" MaxSpeed="99" />      </ItemsControl.Items>                <ItemsControl.Resources>            <DataTemplate DataType="{x:Type clk:ICar}">                <Button Content="{Binding Path=Name}" />            </DataTemplate>      </ItemsControl.Resources>      <ItemsControl.ItemTemplateSelector>            <clk:StandardDataTemplateSelector />      </ItemsControl.ItemTemplateSelector>    </ItemsControl></Window>
namespace BindingInterfaceSample{    public class StandardDataTemplateSelector : DataTemplateSelector    {      // Methods      public override DataTemplate SelectTemplate(object item, DependencyObject container)      {            #region Require            if (item == null) throw new ArgumentNullException();            if (container == null) throw new ArgumentNullException();            #endregion            // Result            DataTemplate itemDataTemplate = null;            // Base DataTemplate            itemDataTemplate = base.SelectTemplate(item, container);            if (itemDataTemplate != null) return itemDataTemplate;            // Interface DataTemplate            FrameworkElement itemContainer = container as FrameworkElement;            if (itemContainer == null) return null;            foreach (Type itemInterface in item.GetType().GetInterfaces())            {                itemDataTemplate = itemContainer.TryFindResource(new DataTemplateKey(itemInterface)) as DataTemplate;                if (itemDataTemplate != null) break;            }            // Return            return itemDataTemplate;      }    }}
http://Files.Dotblogs.com.tw/clark/1205/2012529195352541.png

参考数据
Style、DataTemplate 和隐含索引键
DataTemplateKey 类别


范列下载
BindingInterfaceSample点此下载
页: [1]
查看完整版本: [WPF] DataTemplate Binding to Interface