Skip to content
分类目录:

WPF Template

Post date:
Author:

ControlTemplate

在 WPF 中,控件的 ControlTemplate 用于定义控件的外观。 可以通过定义新的 ControlTemplate 并将其分配给控件来更改控件的结构和外观。

控件模板是通过设置 Control.Template 属性来应用的

https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/how-to-create-apply-template

一般在ControlTemplate中,使用的绑定是TemplateBinding,使用此TemplateBinding后,就无需再指定Source,RelatvieSource或者DataContext

TemplateBinding指向的就是ControlTemplate中的TargetType类型的对象。

注意,在ControlTemplate中,最外层的容器,不要使用TemplateBinding来绑定控件的Margin,Width,Height,HorizontalAlignment,VertialAlignment,Focus,IsHitTestVisible等属性,不然会引发一些问题,

可以这么理解,每个控件都有一个默认的透明的矩形区域,而ControlTemplate定义的是矩形区域内部的控件的显式,控件模板默认会填充满整个矩形区域。

如果给ControlTemplate中的最外层容器,设置Margin TemplateBinding,其实是设置控件模板和默认矩形区域的间距,而不是控件(每个默认的透明的矩形区域)之间的间距,并不能起到效果。

<ControlTemplate x:Key="roundbutton" TargetType="Button">
    <Grid>
        <Ellipse Fill="{TemplateBinding Background}" 
                 Stroke="{TemplateBinding Foreground}" />
        <ContentPresenter 
          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="true">
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
<!-- 在样式中设置ControlTemplate -->
<Style TargetType="Button">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate../>
    </Setter.Value>
  </Setter>
</Style>

<Style TargetType="Button">
  <Setter Property="Template" Value="{StaticResource ...}"/>
</Style>
<StackPanel Margin="10">
    <Label>Unstyled Button</Label>
    <Button>Button 1</Button>
    <Label>Rounded Button</Label>
    <Button 
      Template="{StaticResource roundbutton}">Button 2</Button>
</StackPanel>

但是如果在ControlTemplate中,要绑定到控件本身设置的附加属性上,则使用RelativeSoruce + TemplateBinding。

<ControlTemplate>
    <DockPanel
        Visibility="{Binding Path=(hc:InfoElement.Title)
                        ,RelativeSource={RelativeSource TemplatedParent},
                        Converter={StaticResource String2VisibilityConverter}}" />
</ControlTemplate>

绑定到附加属性:

DataTemplate

常见的DataTemplate:

ItemControls中的ItemTemplate

HeaderedItemsControl 中的HierarchicalDataTemplate ,如MenuItem中,或者TreeView中

一般在DataTemplate中,使用的绑定就是Binding到具体的Field,当把Template应用到具体控件后,就会复用具体控件树上的DateSource。

ItemTemplate一般只能设置每一行的数据展现形式,但是考虑,如果在DataGrid中,要使用CellTemplateSelector,来设置每一列,甚至每一个Cell的单元格数据模板,则在CellTemplateSelector中只能获取到每一行对应的Model,无法获取到该单元格对应的属性,进而无法根据单元格中的值,来设置CellTemplate。

解决方法:配置使用ContentControl和ContentTemplateSelector来设置。

 <DataGrid ItemsSource="{Binding Items}"
    <DataGrid.Columns>
     <DataGridTemplateColumn IsReadOnly="True">                        
         <DataGridTemplateColumn.CellTemplate>
             <DataTemplate>    
                  <!--当使用ContentControl的TemplateSelecotr后,可以实现更佳灵活的变化。 -->
                 <ContentControl Content="{Binding Col1}" 
                   ContentTemplateSelector="{StaticResource CellDataTemplateSelector}"/>
             </DataTemplate>
         </DataGridTemplateColumn.CellTemplate>
     </DataGridTemplateColumn>
    </DataGrid.Columns>	
 </DataGrid>	
<Window x:Class="SDKSample.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="HierarchicalDataTemplate Sample"
  xmlns:src="clr-namespace:SDKSample">
  <DockPanel>
    <DockPanel.Resources>
      <HierarchicalDataTemplate 
            DataType = "{x:Type src:League}"
            ItemsSource = "{Binding Path=Divisions}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>
      <HierarchicalDataTemplate 
           DataType = "{x:Type src:Division}"
           ItemsSource = "{Binding Path=Teams}">
        <TextBlock Text="{Binding Path=Name}"/>
      </HierarchicalDataTemplate>
    </DockPanel.Resources>
    <TreeView>
      <TreeViewItem 
        ItemsSource="{Binding Source={StaticResource MyList}}" 
        Header="My Soccer Leagues" />
    </TreeView>
  </DockPanel>
</Window>

TemplateSelector

public class Animal
{
    public string Name { get; set; }    
    public string Age { get; set; }
}
public class Cat : Animal
 {
     public string CatType { get; set; }
 }

 public class Dog : Animal
 {
     public string DogType { get; set; }
 }
public partial class MainWidnowViewModel:ObservableObject
{
	[ObservableProperty]
    private ObservableCollection<Animal> selectorItems;
	public MainWidnowViewModel()
    {        
        selectorItems = new ObservableCollection<Animal>();
        selectorItems.Add(new Dog
        {
            Age = "1",
            DogType = "DogType12",
            Name = "Dog12"
        });
        selectorItems.Add(new Cat
        {
            Age = "1",
            CatType = "CatType13",
            Name = "Cat13"
        });
    }
}
public class CusListViewItemSelector : DataTemplateSelector
 {
     public DataTemplate DogTemplate { get; set; }
     public DataTemplate CatTemplate { get; set; }

     public override DataTemplate SelectTemplate(object item, DependencyObject container)
     {
         if (item is Dog)
         {
             return DogTemplate;
         }
         else if (item is Cat)
         {
             return CatTemplate;
         }
         return base.SelectTemplate(item, container);
     }
 }
<Window x:Class="WpfFeature.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:model="clr-namespace:WpfFeature.Models"
        xmlns:selector="clr-namespace:WpfFeature.STS"
        mc:Ignorable="d"
        DataContext="{StaticResource mainViewModel}"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
      <!--定义DataTemplate -->
        <DataTemplate DataType="{x:Type model:Dog}" x:Key="DogTemp">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding DogType}" Foreground="Green"/>
                <TextBlock Text="{Binding Name}" Foreground="Green"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type model:Cat}" x:Key="CatTemp">
            <StackPanel Orientation="Vertical">
                <RadioButton Content="{Binding CatType}" Foreground="Orange"/>
                <TextBlock Text="{Binding Name}" Foreground="Green"/>
            </StackPanel>
        </DataTemplate>
      	<!--定义Selector -->
        <selector:CusListViewItemSelector x:Key="cusListViewSelector" 
          DogTemplate="{StaticResource DogTemp}" 
          CatTemplate="{StaticResource ResourceKey=CatTemp}"/>
    </Window.Resources>

   <ListView ItemsSource="{Binding SelectorItems}" 
     ItemTemplateSelector="{StaticResource cusListViewSelector}">
   </ListView>
</Window>

TemplateSelector,DataTrigger,Control中的子部件通过MultiBinding和Converter单独控制状态,

三种方式均能实现,某属性改变后,修改整个自定义控件的外观。

TemplateSelector:只能绑定Content或者Item,无法绑定更多的数据

豫ICP备2021008859号-1