数据模板选择器主要运用在一些项容器中用于根据不同的数据类型选择不同的DataTemplate,以便展示不同的数据。
核心是override重写SelectTemplate方法,以判断指定数据返回不同的模板。
同样的效果也可以直接使用DataTrigger控制Visibility属性实现,但是Selector更加优雅,维护性更好。
下面两种应用,使用两种不同的写法。
应用1:TreeView树目录不同层级不同模板
目录树实现的全部逻辑和动图效果查看此篇
共3个层级root,menu,detail,三个层级有不同的模板。因此在资源中定义三种HierarchicalDataTemplate控件模板,模板中为DataType赋值,这是使用Selector的关键;
DataType对应三个类名RootOrgNodeViewModel、MenuOrgNodeViewModel、DetailOrgNodeViewModel。都继承于OrgNodeViewModel。
<HierarchicalDataTemplate x:Key="rootNodeTemplate"
ItemsSource="{Binding Children}"
DataType="{x:Type vm:RootOrgNodeViewModel}">
<HierarchicalDataTemplate x:Key="menuNodeTemplate"
ItemsSource="{Binding Children}"
DataType="{x:Type vm:MenuOrgNodeViewModel}">
<HierarchicalDataTemplate x:Key="detailNodeTemplate"
ItemsSource="{Binding Children}"
DataType="{x:Type vm:DetailOrgNodeViewModel}">
资源中声明DataTemplateSelector选择器,并应用在TreeView中
<LocalTmeplate:ContextMenuDataTemplateSelector
x:Key="ContextMenuDataTemplateSelector"/>
<TreeView Height="365" ItemsSource="{Binding Root}"
ItemTemplateSelector="{StaticResource ContextMenuDataTemplateSelector}"/>
完整xaml代码
<UserControl .. x:Name="this">
<UserControl.Resources>
<vm:BindingProxy x:Key="Proxy" Data="{Binding}" />
<LocalTmeplate:ContextMenuDataTemplateSelector x:Key="ContextMenuDataTemplateSelector"/>
<ContextMenu x:Key="Tree_menuNodeMenu">
<MenuItem Name="Add" Command="{Binding Source={StaticResource Proxy}, Path=Data.AddNodeCommand}" CommandParameter="{Binding}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<Label Content="新增"/>
<TextBox Width="20" Text="{Binding Source={StaticResource Proxy}, Path=Data.AddCount}"
Height="20" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Label Content="个子节点"/>
</StackPanel>
</MenuItem.Header>
</MenuItem>
</ContextMenu>
<ContextMenu x:Key="Tree_detailNodeMenu">
<MenuItem Header="删除" Name="Delete_self"
Command="{Binding Source={StaticResource Proxy}, Path=Data.DeleteNodeCommand}" CommandParameter="{Binding}"/>
<Separator/>
<MenuItem Header="在上方插入" Name="Insert_up"
Command="{Binding Source={StaticResource Proxy}, Path=Data.InsertBeforeNodeCommand}" CommandParameter="{Binding}"/>
<MenuItem Header="在下方插入" Name="Insert_down"
Command="{Binding Source={StaticResource Proxy}, Path=Data.InsertAfterNodeCommand}" CommandParameter="{Binding}"/>
<Separator/>
<MenuItem Header="上移" Name="swap_up"
Command="{Binding Source={StaticResource Proxy}, Path=Data.SwapBeforeNodeCommand}" CommandParameter="{Binding}"/>
<MenuItem Header="下移" Name="swap_down"
Command="{Binding Source={StaticResource Proxy}, Path=Data.SwapAfterNodeCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
<HierarchicalDataTemplate x:Key="menuNodeTemplate" ItemsSource="{Binding Children}" DataType="{x:Type vm:MenuOrgNodeViewModel}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource Tree_menuNodeMenu}">
<Label Content="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="detailNodeTemplate" ItemsSource="{Binding Children}" DataType="{x:Type vm:DetailOrgNodeViewModel}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource Tree_detailNodeMenu}">
<Label >
<Label.Content>
<TextBlock Text="{Binding Index,StringFormat={}[{0}号]}"/>
</Label.Content>
</Label>
<Label Content="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="rootNodeTemplate" ItemsSource="{Binding Children}" DataType="{x:Type vm:RootOrgNodeViewModel}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Name}"/>
</StackPanel>
</HierarchicalDataTemplate>
</UserControl.Resources>
<StackPanel >
<TreeView Height="365" ItemsSource="{Binding Root}" ItemTemplateSelector="{StaticResource ContextMenuDataTemplateSelector}"/>
</StackPanel>
</UserControl>
定义Selector的逻辑
判断每个节点的类型,返回不同的模板
public class ContextMenuDataTemplateSelector : DataTemplateSelector
{
//item表示TreeView数据源中每个Item
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
DataTemplate template = null;
if (item is MenuOrgNodeViewModel)
{
template = element.FindResource("menuNodeTemplate") as HierarchicalDataTemplate;
}
else if (item is RootOrgNodeViewModel)
{
template = element.FindResource("rootNodeTemplate") as HierarchicalDataTemplate;
}
else if (item is DetailOrgNodeViewModel)
{
template = element.FindResource("detailNodeTemplate") as DataTemplate;
}
return template;
}
}
定义每个节点对应的类型
public class MenuOrgNodeViewModel : OrgNodeViewModel
{
internal MenuOrgNodeViewModel(Node i, OrgNodeViewModel parent = null) : base(i, parent)
{
}
}
public class RootOrgNodeViewModel : OrgNodeViewModel
{
internal RootOrgNodeViewModel(Node i, OrgNodeViewModel parent = null) : base(i, parent)
{
}
}
public class DetailOrgNodeViewModel : OrgNodeViewModel
{
internal DetailOrgNodeViewModel(Node i, OrgNodeViewModel parent = null) : base(i, parent)
{
}
}
应用2:DataGrid表格单元格内使用不同模板
表格的全部逻辑和动图效果查看此篇,内含两种实现单元格编辑时为不同控件的方法
根据数据的类型单元格在编辑模式下,可以显示文本框、下拉框、弹窗按钮还有不可编辑单元格。
定义四种模板
<local:CellEditTemplateSelector.TextBoxTemplate>
<local:CellEditTemplateSelector.ComboxTemplate>
<local:CellEditTemplateSelector.PopupButtonTemplate>
<local:CellEditTemplateSelector.UnEnableTemplate>
将选择器应用在表格所在列的CellEditingTemplateSelector上
<local:CellEditTemplateSelector x:Key="CellEditTemplateSelector">
<DataGridTemplateColumn Header="设定值"
CellEditingTemplateSelector="{StaticResource CellEditTemplateSelector}">
选择器相关的关键xaml代码
<UserControl.Resources>
<local:SetValueTextEditingConverter x:Key="SetValueTextEditingConverter"/>
<local:CellEditTemplateSelector x:Key="CellEditTemplateSelector">
<local:CellEditTemplateSelector.TextBoxTemplate>
<DataTemplate>
<TextBox>
<TextBox.Text>
<MultiBinding Converter="{StaticResource SetValueTextEditingConverter}">
<Binding Path="setValue" UpdateSourceTrigger="PropertyChanged" />
<Binding Path="decimalPlaces"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</local:CellEditTemplateSelector.TextBoxTemplate>
<local:CellEditTemplateSelector.ComboxTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding valueOptions}"
SelectedItem="{Binding selectedOptionItem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding fullDetail}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</local:CellEditTemplateSelector.ComboxTemplate>
<local:CellEditTemplateSelector.PopupButtonTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding setValue,Mode=TwoWay}"/>
<Button Width="20" Content="..." HorizontalAlignment="Right"
Command="{Binding EditChildValueCommand}"
CommandParameter="{Binding}"/>
</Grid>
</DataTemplate>
</local:CellEditTemplateSelector.PopupButtonTemplate>
<local:CellEditTemplateSelector.UnEnableTemplate>
<DataTemplate>
<TextBlock Text="--"/>
</DataTemplate>
</local:CellEditTemplateSelector.UnEnableTemplate>
</local:CellEditTemplateSelector>
...
<DataGridTemplateColumn Header="设定值" CellEditingTemplateSelector="{StaticResource CellEditTemplateSelector}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
...
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</UserControl.Resources>
选择器逻辑
public class CellEditTemplateSelector : DataTemplateSelector
{
public DataTemplate TextBoxTemplate { get; set; }
public DataTemplate ComboxTemplate { get; set; }
public DataTemplate PopupButtonTemplate { get; set; }
public DataTemplate UnEnableTemplate { get; set; }
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container) {
//item为表格每行数据源,选择模板的关键是判断数据源中editType和valueType两个属性值
if (item is GirdDataModel) {
GirdDataModel data = item as GirdDataModel;
if (data.editType == "不可修改") return UnEnableTemplate;
switch (data.valueType)
{
case ValueTypeEnum.Number:
return TextBoxTemplate;
case ValueTypeEnum.Option:
return ComboxTemplate;
case ValueTypeEnum.Object:
return PopupButtonTemplate;
}
}
return base.SelectTemplate(item, container);
}
}