上一篇简单扩展了TabControl的ItemsSource属性。只是只能应用一个统一的模板展示数据,但是毕竟ItemsSource是IEnumerable类型的,这就意味着里面的每个Item都可以有自己不同的Type,而相同的DataTemolate可能并不适用。
在WPF和SL5中可以给DataTemplate标记DataType,这样ItemsControl会去自己识别相应的类型所需的模板。在SL4中也让我们来简单实践下。
在TabControlExtensions类里增加一个DataTemplate的附加属性,实验证明Type类型并不能很好的被编译器识别,还是用string了。


public static string GetDataTypeName(DependencyObject d)
{
return ( string )d.GetValue(DataTypeNameProperty);
}
public static void SetDataTypeName(DependencyObject d, string value)
{
d.SetValue(DataTypeNameProperty, value);
}
public static readonly DependencyProperty DataTypeNameProperty =
DependencyProperty.Register( " DataTypeName " , typeof ( string ), typeof (DataTemplate),
new PropertyMetadata(OnDataTypeNameChanged));
private static void OnDataTypeNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var template = d as DataTemplate;
var typeName = ( string )e.NewValue;
if ( ! string .IsNullOrEmpty(typeName))
{
_templateStore[typeName] = template;
}
}
在回调方法里把相应的键值存储到一个静态字典里,以便以后取用。
稍微改一下上次的那个Animal类,继承增加三个不同的类型。


{
public abstract string Name { get ; }
public int Count { get ; set ; }
public string From { get ; set ; }
}
public class Zabra : Animal
{
public override string Name
{
get { return " 斑马 " ; }
}
}
public class Penguin : Animal
{
public override string Name
{
get { return " 企鹅 " ; }
}
}
public class Lemur : Animal
{
public override string Name
{
get { return " 环尾狐猴 " ; }
}
}
然后就是在Resources里为三个不同的类型增加三个不同的DataTemplate。


< DataTemplate x:Key ="s" local:TabControlExtensions.DataTypeName ="Penguin" >
< Grid Background ="Black" >
< Grid.RowDefinitions >
< RowDefinition Height ="0.2*" />
< RowDefinition Height ="0.6*" />
< RowDefinition Height ="0.2*" />
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition Width ="0.2*" />
< ColumnDefinition Width ="0.6*" />
< ColumnDefinition Width ="0.2*" />
</ Grid.ColumnDefinitions >
< Rectangle Grid.Column ="1" Fill ="White" Grid.Row ="1" />
</ Grid >
</ DataTemplate >
< DataTemplate x:Key ="ss" local:TabControlExtensions.DataTypeName ="Zabra" >
< Grid >
< Grid.Background >
< LinearGradientBrush EndPoint ="20,20" SpreadMethod ="Repeat" MappingMode ="Absolute" >
< GradientStop Color ="Black" Offset ="0" />
< GradientStop Color ="Black" Offset ="0.5" />
< GradientStop Color ="White" Offset ="0.5" />
< GradientStop Color ="White" Offset ="1" />
</ LinearGradientBrush >
</ Grid.Background >
</ Grid >
</ DataTemplate >
< DataTemplate x:Key ="sss" local:TabControlExtensions.DataTypeName ="Lemur" >
< Grid >
< Grid.ColumnDefinitions >
< ColumnDefinition Width ="0.5*" />
< ColumnDefinition Width ="0.5*" />
</ Grid.ColumnDefinitions >
< Rectangle Grid.Column ="1" >
< Rectangle.Fill >
< LinearGradientBrush EndPoint ="0,0" StartPoint ="30,0" SpreadMethod ="Repeat" MappingMode ="Absolute" >
< GradientStop Color ="Black" Offset ="0" />
< GradientStop Color ="Black" Offset ="0.5" />
< GradientStop Color ="White" Offset ="0.5" />
< GradientStop Color ="White" Offset ="1" />
</ LinearGradientBrush >
</ Rectangle.Fill >
</ Rectangle >
</ Grid >
</ DataTemplate >
</ UserControl.Resources >
x:key随便写一下,并为每个DataTemplate设置local:TabControlExtensions.DataTypeName附加属性,属性的值为对应的需要呈现此模板的类名,可见第一个模板是给企鹅的,第二个是给斑马的,第三个是给狐猴的。
最后只要对上次的那个OnItemsSourceChanged方法稍作调整就行了。


{
var source = d as TabControl;
var items = e.NewValue as IEnumerable;
source.Items.Clear();
if (items != null )
{
var headerTemplate = GetHeaderTemplate(source);
var contentTemplate = GetContentTempalte(source);
foreach (var item in items)
{
// -------
var typeName = item.GetType().Name;
var itemTemplate = _templateStore.ContainsKey(typeName) ? _templateStore[typeName] : null ;
// -------
var tabItem = new TabItem
{
DataContext = item,
Header = item,
HeaderTemplate = headerTemplate,
Content = item,
// ---------
ContentTemplate = itemTemplate ?? contentTemplate,
// ---------
};
source.Items.Add(tabItem);
}
}
}
增加的部分代码用注释标记了,从存储了键值的字典里根据Type的Name获取相应的模板,很简单。
看下效果,条纹斑马:
白肚皮黑身体的企鹅:
一圈一圈尾巴的环尾狐猴:
以及代码:这里