WPF中文网

ControlTemplate的触发器

我们来观察一下ControlTemplate控件模块的定义

public class ControlTemplate : FrameworkTemplate
{
    public ControlTemplate();
    public ControlTemplate(Type targetType);

    public Type TargetType { get; set; }
    public TriggerCollection Triggers { get; }

    protected override void ValidateTemplatedParent(FrameworkElement templatedParent);

}

可以看到在ControlTemplate中定义了一个Triggers 集合,说明可以定义一些触发器,以实现控件的交互效果。我们之前在《样式》章节中学过几种触发器,在这里用例子演示一下ControlTemplate的触发器的用法。

我们将上一章节的例子进行一些微调,因为在使用触发器时,有些细节需要注意。

一、在控件中的ControlTemplate的触发器

<Button Content="将ControlTemplate定义在在控件中" 
        Width="280" 
        Height="40" 
        Margin="10" 
        Foreground="#747787">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border x:Name="border" 
                    Background="Transparent" 
                    CornerRadius="5" 
                    BorderThickness="1" 
                    BorderBrush="#C9CCD5">
                <ContentPresenter x:Name="contentPresenter" 
                                  HorizontalAlignment="Center" 
                                  VerticalAlignment="Center"/>
            </Border>
            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Content" Value="MouseOver" TargetName="contentPresenter"/>
                </Trigger>
                <Trigger Property="IsMouseOver" Value="False">
                    <Setter Property="Content" Value="将ControlTemplate定义在在控件中" TargetName="contentPresenter"/>
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Button.Template>
</Button>

在上面的例子中,我们在Triggers集合中增加了两个Trigger 对象,条件是当鼠标移上去或鼠标移开的时候,更改了Button的Content属性。

二、在Resources定义的ControlTemplate的触发器

<Window.Resources>
    <ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
        <Border Background="#C6D2FC" 
                CornerRadius="5" 
                BorderThickness="1" 
                BorderBrush="#545BAD">
            <ContentPresenter  HorizontalAlignment="Center" 
                               VerticalAlignment="Center"/>
        </Border>
        <ControlTemplate.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Width" Value="300"/>
            </Trigger>
            <Trigger Property="IsMouseOver" Value="False">
                <Setter Property="Width" Value="280"/>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>
</Window.Resources>

<Button Content="将ControlTemplate定义在资源中" 
        Template="{StaticResource ButtonTemplate}" 
        Height="40" Margin="10" Foreground="#707CA5"/>

上面演示了定义在资源中的控件模板的触发器,需要注意的一点是,我们在触发器执行时更改了Button的宽度,这个时候,要把Button原先设置的Width删除,因为”就近原则“,直接设置控件的属性值大于天,而在模板中设置控件的属性值的权重要小一些。

三、Style样式中的ConControlTemplate的触发器

<Button Content="将ControlTemplate定义在Style样式中" 
        Width="280" Height="40" Margin="10" >
    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border x:Name="border" 
                                CornerRadius="5" 
                                BorderThickness="1" 
                                BorderBrush="#7AAB7D">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="auto"/>
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Grid.Column="0" Text="☻" 
                                           VerticalAlignment="Center" 
                                           Margin="3" FontSize="18"/>
                                <ContentPresenter Grid.Column="1" 
                                                  HorizontalAlignment="Center" 
                                                  VerticalAlignment="Center"/>
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Foreground" Value="#7AAB7D"/>
                                <Setter Property="Background" Value="White" TargetName="border"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="False">
                                <Setter Property="Foreground" Value="White"/>
                                <Setter Property="Background" Value="#7AAB7D" TargetName="border"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Button.Style>
</Button>

ControlTemplate中使用触发器,还有一个好处是:可以指定设置某个可视化树中的控件对象。比如上面这段代码,当鼠标移上去的时候,我们修改了border对象的背景色,这个背景色其实并不是Button本身的背景颜色,而是Button内部可视化树中的Border的背景颜色。只需要利用Setter的TargetName属性来指定就行了。

而鼠标移上去的时候,我们还修改了Foreground前景颜色,这个Foreground才是Button本身的属性。说明什么问题?说明ControlTemplate中的触发器不但可以修改控件的属性,还可以修改控件模板中的可视化树的元素的属性,它真是太好用了。

我们来看一下运行结果。

鼠标移上去之前
鼠标移上去之后

要充分了解ControlTemplate控件模板的定义和触发器的使用,还离不开TemplateBinding(模板绑定),它将控件的属性和控件的可视化树元素的属性建立绑定关系,在设计控件模板时,可以更好的设计出控件的结构、外观和交互效果。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:064-《ControlTemplate的触发器》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年9月28日

当学会了DataTrigger数据触发器和MultiTrigger多条件触发器,MultiDataTrigger就比较好理解了,它就是前面两个触发器的结合体。

public sealed class MultiDataTrigger : TriggerBase, IAddChild
{
    public MultiDataTrigger();

    public ConditionCollection Conditions { get; }
    public SetterBaseCollection Setters { get; }

}

从定义上看,它与MultiTrigger多条件触发器一模一样,只是内部的实现略有不同。我们可以将一节的例子稍作修改,来演示一下MultiDataTrigger的用法。

<DataGrid ItemsSource="{Binding Persons}" SelectedItem="{Binding Person}" AutoGenerateColumns="False">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Age}" Value="19">
                    <Setter Property="Background" Value="LightBlue"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Age}" Value="20">
                    <Setter Property="Background" Value="LightGreen"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Age}" Value="21">
                    <Setter Property="Background" Value="LightCoral"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=_CheckBox,Path=IsChecked}" Value="True">
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="FontSize" Value="16"/>
                    <Setter Property="FontWeight" Value="Bold"/>
                </DataTrigger>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=Age}" Value="20"/>
                        <Condition Binding="{Binding Path=Name}" Value="张三"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Foreground" Value="Red"/>
                        <Setter Property="FontSize" Value="16"/>
                        <Setter Property="FontWeight" Value="Bold"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
    <DataGrid.Columns>
        <DataGridTextColumn Header="姓名" Binding="{Binding Name}"/>
        <DataGridTextColumn Header="年龄" Binding="{Binding Age}"/>
        <DataGridTextColumn Header="生日" Binding="{Binding Address}"/>
    </DataGrid.Columns>
    <!--<ListView.View>
        <GridView>
            <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="60"/>
            <GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="auto"/>
            <GridViewColumn Header="地址" DisplayMemberBinding="{Binding Address}" Width="auto"/>
        </GridView>
    </ListView.View>-->
</DataGrid>

在上面的前端代码中,我们增加了一个MultiDataTrigger触发器的实例。

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Path=Age}" Value="20"/>
        <Condition Binding="{Binding Path=Name}" Value="张三"/>
    </MultiDataTrigger.Conditions>
    <MultiDataTrigger.Setters>
        <Setter Property="Foreground" Value="Red"/>
        <Setter Property="FontSize" Value="16"/>
        <Setter Property="FontWeight" Value="Bold"/>
    </MultiDataTrigger.Setters>
</MultiDataTrigger>

条件是,当Person是一个20岁的“张三”时,把当前行的字体进行了一些特别设置。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:059-《MultiDataTrigger数据触发器》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年9月18日

DataTrigger数据触发器,它会在绑定数据满足指定条件时应用属性值或执行操作。

public class DataTrigger : TriggerBase, IAddChild
{
    public DataTrigger();

    public BindingBase Binding { get; set; }
    public object Value { get; set; }
    public SetterBaseCollection Setters { get; }

    public static void ReceiveMarkupExtension(object targetObject, XamlSetMarkupExtensionEventArgs eventArgs);

}

DataTrigger拥有一个Binding属性,表明它可以绑定某个控件的属性,或者是某个ViewModel的属性,Value属性则是表示绑定的属性达到某个值时,触发条件成立,然后去执行Setters集合里面的内容。

接下来,我们以一个例子来演示DataTrigger绑定ViewModel属性以及DataTrigger绑定控件属性并触发相应的操作。

前端代码

<DataGrid ItemsSource="{Binding Persons}" SelectedItem="{Binding Person}" AutoGenerateColumns="False">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Age}" Value="19">
                    <Setter Property="Background" Value="LightBlue"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Age}" Value="20">
                    <Setter Property="Background" Value="LightGreen"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding Age}" Value="21">
                    <Setter Property="Background" Value="LightCoral"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=_CheckBox,Path=IsChecked}" Value="True">
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="FontSize" Value="16"/>
                    <Setter Property="FontWeight" Value="Bold"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
    <DataGrid.Columns>
        <DataGridTextColumn Header="姓名" Binding="{Binding Name}"/>
        <DataGridTextColumn Header="年龄" Binding="{Binding Age}"/>
        <DataGridTextColumn Header="生日" Binding="{Binding Address}"/>
    </DataGrid.Columns>
    <!--<ListView.View>
        <GridView>
            <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="60"/>
            <GridViewColumn Header="年龄" DisplayMemberBinding="{Binding Age}" Width="auto"/>
            <GridViewColumn Header="地址" DisplayMemberBinding="{Binding Address}" Width="auto"/>
        </GridView>
    </ListView.View>-->
</DataGrid>

我们将前面的例子稍做修改,采用DataGrid控件绑定Persons集合,在DataGridRow的Style样式中,实例化了4个DataTrigger,前端3个DataTrigger的条件是:当Person.Age等于19、20、21时,将当前行的背景颜色分别改成LightBlue、LightGreen和LightCoral。

注意最后一个DataTrigger,它绑定了CheckBox的IsChecked属性,当IsChecked=True时,将当前行的字体属性进行了一些设置。

最后,我们来生成一些Penson元素,看看前端的呈现效果。

当CheckBox被选中后,即IsChecked=True,此时会将每一行的字体的属性都进行改动。

完整的代码请在下方下载。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:058-《DataTrigger数据触发器》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年9月18日

MultiTrigger表示多个条件同时满足时才会触发的触发器。

public sealed class MultiTrigger : TriggerBase, IAddChild
{
    public MultiTrigger();

    public ConditionCollection Conditions { get; }
    public SetterBaseCollection Setters { get; }

}

一、属性成员

1.1 Conditions属性

MultiTrigger的Conditions属性是一个集合,表示条件。这个集合的元素是Condition类型。

public sealed class Condition : ISupportInitialize
{
    public Condition();
    public Condition(DependencyProperty conditionProperty, object conditionValue);
    public Condition(BindingBase binding, object conditionValue);
    public Condition(DependencyProperty conditionProperty, object conditionValue, string sourceName);

    public DependencyProperty Property { get; set; }
    public BindingBase Binding { get; set; }
    public object Value { get; set; }
    public string SourceName { get; set; }

    public static void ReceiveMarkupExtension(object targetObject, XamlSetMarkupExtensionEventArgs eventArgs);
    public static void ReceiveTypeConverter(object targetObject, XamlSetTypeConverterEventArgs eventArgs);

}

通常情况下,我们只需要使用Property属性和Value属性即可,即“某个属性达到某个值时”这样一种条件定义。

<Condition Property="IsMouseOver" Value="True"/>

1.2 Setters属性

MultiTrigger的Setters属性也是一个集合,表示触发器触发后要设置的项。比如:

                                
<Setter Property="Foreground" Value="Red"/>

二、MultiTrigger示例

<CheckBox x:Name="checkbox">
    <CheckBox.Style>
        <Style TargetType="CheckBox">
            <Setter Property="Content" Value="MultiTrigger"/>
            <Setter Property="Width" Value="150"/>
            <Setter Property="Height" Value="30"/>
            <Setter Property="Background" Value="Orange"/>
            <Setter Property="Foreground" Value="Green"/>
            <Setter Property="Margin" Value="3"/>
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True"/>
                        <Condition Property="IsChecked" Value="True"/>
                    </MultiTrigger.Conditions>
                    <MultiTrigger.Setters>
                        <Setter Property="Foreground" Value="Red"/>
                        <Setter Property="Content" Value="多条件触发器"/>
                    </MultiTrigger.Setters>
                </MultiTrigger>                        
            </Style.Triggers>
        </Style>
    </CheckBox.Style>
</CheckBox>        

在上面的例子中,我们通过Style样式设置了CheckBox的Content及其它属性。然后在样式中实例化了一个MultiTrigger多条件触发器,并为其Conditions集合增加了两个条件,分别是IsMouseOver等于True和IsChecked等于True,当条件满足时,会改变CheckBox的前景色为红色,同时,Content变成"多条件触发器"。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:051-《MultiTrigger多条件触发器》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年9月13日

触发器是指当满足预设的条件时去执行一些事务的工具,比如我们希望鼠标移到某个按钮上方时,这个按钮的颜色、大小发生一些改变。这个时候,条件是鼠标移到按钮上,执行的事务是改变按钮的颜色和大小。

WPF提供了5种触发器,以满足不同场合下的使用要求。触发器主要运用的场景在Style、ControlTemplate、DataTemplate三个地方。我们先介绍Style样式中的用法,待学习模板知识时,再进一步学习触发器在模板中的使用。

触发器名称说明
Trigger监测依赖属性的变化、触发器生效
MultiTrigger通过多个条件的设置、达到满足条件、触发器生效
DataTrigger通过数据的变化、触发器生效
MultiDataTrigger多个数据条件的触发器
EventTrigger事件触发器, 触发了某类事件时, 触发器生效。

一、Trigger触发器的定义

public class Trigger : TriggerBase, IAddChild, ISupportInitialize
{
    public Trigger();

    public DependencyProperty Property { get; set; }
    public object Value { get; set; }
    public string SourceName { get; set; }
    public SetterBaseCollection Setters { get; }

    public static void ReceiveTypeConverter(object targetObject, XamlSetTypeConverterEventArgs eventArgs);

}

从定义上看,它有4个属性,我们一一分析

Property属性:表示定义触发器所指向的属性名称,需要注意的是,它的类似是DependencyProperty(依赖属性),也就是说,触发器所生效的属性必须是WPF中的依赖属性。

Value属性:表示定义触发器所指向的属性的值,这两个属性要连起来理解,即某个属性的值等于预设的值时,此时将该触发器将被触发,至于要执行哪些具体的事务,就要看Setters集合中定义了哪些项。

SourceName属性:获取或设置与导致关联的 setter 要应用的属性对象的名称。

Setters属性:这是一个集合,类似于Style样式的Setters,且用途是一致的,就是指当前触发器被触发时,将执行Setters里面的内容项。

二、Trigger示例

前端代码

<Window x:Class="HelloWorld.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:local="clr-namespace:HelloWorld" 
        xmlns:forms="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
        mc:Ignorable="d" FontSize="14"
        Title="WPF中文网之控件课程 - www.wpfsoft.com" Height="350" Width="500">
    <Window.Resources>
        <Style x:Key="GreenButtonStyle" TargetType="Button">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="30"/>
            <Setter Property="Background" Value="Green"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="Margin" Value="3"/>
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Foreground" Value="Red"/>
                    <Setter Property="Width" Value="150"/>
                    <Setter Property="Height" Value="50"/>
                    <Setter Property="Content" Value="鼠标移入"/>
                </Trigger>                
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel VerticalAlignment="Center">
        <Button Content="绿色按钮" Style="{StaticResource GreenButtonStyle}"/>
        <CheckBox Content="CheckBox">
            <CheckBox.Style>
                <Style TargetType="CheckBox">
                    <Setter Property="Width" Value="100"/>
                    <Setter Property="Height" Value="30"/>
                    <Setter Property="Background" Value="Orange"/>
                    <Setter Property="Foreground" Value="Green"/>
                    <Setter Property="Margin" Value="3"/>
                    <Style.Triggers>
                        <Trigger Property="IsChecked"  Value="True">
                            <Setter Property="Foreground" Value="Red"/>
                        </Trigger>
                        <Trigger Property="IsChecked"  Value="False">
                            <Setter Property="Foreground" Value="Orange"/>
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="FontWeight" Value="Bold"/>
                        </Trigger>                       
                        <Trigger Property="IsMouseOver" Value="False">
                            <Setter Property="FontWeight" Value="Normal"/>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </CheckBox.Style>
        </CheckBox> 
    </StackPanel>
</Window>

在按钮的样式中,我们定义了一个触发器,条件是当按钮的IsMouseOver属性等于True时,执行的事务是将按钮的前景色改成红色,同时改变按钮的尺寸。

在CheckBox复选项的样式中,我们定义的触发器条件是当CheckBox的IsChecked属性等于True时,执行的事务是将复选框的前景色改成红色。

需要注意的是:Trigger触发器的条件应该是当前控件拥有的属性名称。比如Button按钮没有IsChecked属性,就不可设置与CheckBox相同的触发器。

大多数控件都有IsMouseOver属性,包括CheckBox,所以,我们在鼠标移到CheckBox上方时,将其字体变成了粗体。另外,完整的触发器写法应如CheckBox一样,如果是bool型的属性,Value的值就有两个,分别是True和False,所以,条件也就有了两个,如果只写了True的情况,WPF也会默认一个False条件的触发器。

下一节,我将介绍多条件的触发器。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:050-《Trigger触发器》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年9月11日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1