WPF样式及其触发器

WPF的样式–style直译过来就是“风格”,“样式”。如果把WPF窗体看做一个舞台,那么窗体上的控件就是一个个演员,它们的职责就是在用户界面上按照业务逻辑的需要扮演自己的角色。为了让同一种控件能担当起不同的角色,程序员就要为它们设计多种外观样式和行为动作,这就是Style。构成Style最重要的两种元素是Setter和Trigger,Setter类帮助我们设置控件的静态外观风格,Trigger类则帮助我们设置控件的行为风格。
Style中的触发器
Setter,设置器。什么的设置器呢?属性值的。我们给属性赋值的时候一般都采用“属性名=属性值”的形式。Setter类的Property属性用来指明你想为目标的那个属性赋值; Setter类的Value属性则是你提供的属性值。
下面的例子中在Window的资源词典中放置一个针对TextBlock的Style,Style中使用若干
Setter来设定TextBlock的一些属性,这样程序中的TextBlock就会具有统一的风格,除非
你使用{x:Null}显示地清空Style。
XAML代码如下:

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <!--定义TextBlock样式-->
        <Style TargetType="TextBlock">
            <Style.Setters>
                <!--字体大小:24-->
                <Setter Property="FontSize" Value="24"/>
                <!--文本装饰:下划线-->
                <Setter Property="TextDecorations" Value="Underline"/>
                <!--字体样式:斜体-->
                <Setter Property="FontStyle" Value="Italic"/>
            </Style.Setters>
        </Style>
    </Window.Resources>
    <StackPanel Margin="5">
        <TextBlock Text="Hello WPF"/>
        <TextBlock Text="This is a sample for Style!"/>
        <!--使用{x:Null}显示地清空Style-->
        <TextBlock Text="by Tim 2020.06.19" Style="{x:Null}"/>
    </StackPanel>
</Window>;

因为Style的内容属性是Setters,所以我们可以直接在

<Style TargetType="TextBlock"> 
<!--字体大小:24--> 
<Setter Property="FontSize" Value="24"/> 
<!--文本装饰:下划线--> 
<Setter Property="TextDecorations" Value="Underline"/> 
<!--字体样式:斜体--> 
<Setter Property="FontStyle" Value="Italic"/> 
</Style>

Style中的Trigger
Trigger,触发器,即当某些条件满足时会触发一个行为(比如某些值的变化或动画的
发生等)。触发器比较像事件。事件一般是由用户操作触发的,而触发器除了有事件触
发型的EventTrigger外还有数据变化触发型的Trigger/DataTrigger及多条件触发型的
MultiTrigger/MultiDataTrigger等。
触发器会有以下几种的Trigger:基本Trigger、MultiTrigger、由数据触发的DataTrigger、多数据条件触发的MultiTrigger、由事件触发的EventTrigger。
第一种,基本触发器Trigger
Trigger类是最基本的触发器。类似于Setter,Trigger也有Property和Value这两个属
性,Property是Trigger关注的属性名称,Value是触发条件。Trigger类还有一个Setter属
性,此属性值是一组Setter,一旦触发条件被满足,这组Setter的“属性—值”就会被应用,
触发条件不在满足后,各属性值会被还原。
下面这个例子中包含一个针对CheckBox的Style,当CheckBox的IsChecked属性为true
的时候前景色和字体会改变。XAML代码如下:

<Window.Resources> 
<Style TargetType="CheckBox"> 
<Style.Triggers> 
<Trigger Property="IsChecked" Value="true"> 
<Setter Property="FontSize" Value="20"/> 
<Setter Property="Foreground" Value="Orange"/> 
</Trigger> 
</Style.Triggers> 
</Style> 
</Window.Resources> 
<StackPanel Margin="5"> 
<CheckBox Content="悄悄的我走了" Margin="5"/> 
<CheckBox Content="正如我悄悄的来" Margin="5,0"/> 
<CheckBox Content="我挥一挥衣袖" Margin="5"/> 
<CheckBox Content="不带走一片云彩" Margin="5,0"/> 
</StackPanel>

第二种,MultiTrigger
MultiTrigger是个容易让人误解的名字,会让人以为是多个Trigger集成在一起,实际
上叫MultiConditionTrigger更合适,因为必须多个条件同时成立时才会被触发。
MultiTrigger比Trigger多了一个Conditions属性,需要同时成立的条件就存储在这个集合
中。
让我们稍微改动一下上面的例子,要求同时满足CheckBox被选中且Content为“正如
我悄悄的来”时才会被触发。XAML代码如下(仅style部分):

<Style TargetType="CheckBox">
        <Style.Triggers>
            <!--多条件触发器-->
            <MultiTrigger>
                <!--1、条件存储的集合-->
                <MultiTrigger.Conditions>
                    <!--条件:1:复选框选中状态-->
                    <Condition Property="IsChecked" Value="true"/>
                    <!--条件2:复选框内容是“正如我悄悄的来”-->
                    <Condition Property="Content" Value="正如我悄悄的来"/>
                </MultiTrigger.Conditions>
                <!--2、多个条件同时成立触发-->
                <MultiTrigger.Setters>
                    <!--字体大小:20-->
                    <Setter Property="FontSize" Value="20"/>
                    <!--字体颜色:橙色-->
                    <Setter Property="Foreground" Value="Orange"/>
                </MultiTrigger.Setters>
            </MultiTrigger>
        </Style.Triggers>
   </Style>

第三种,由数据触发的DataTrigger
程序中经常会遇到基于数据执行某些判断情况,遇到这种情况时我们可以考虑使用
DataTrigger。DataTrigger对象的Binding属性会把数据源源不断送过来,一旦送来的值与
Value属性一致,DataTrigger即被触发。
下面例子中,当TextBox的Text长度小于7个字符时其Border会保持红色。XAML代码
如下:

<!--复选框CheckBox样式-->
    <Window.Resources>
        <local:L2BConverter x:Key="cvtr"/>
        <Style TargetType="TextBox">
            <Style.Triggers>
                <!--数据触发器:Value="false"-->
                <DataTrigger Binding="{Binding RelativeSource={x:Static RelativeSource.Self},Path=Text.Length,Converter={StaticResource cvtr}}" Value="false">
                    <!--边框:红色-->
                    <Setter Property="BorderBrush" Value="Red"/>
                    <!--边框粗度:1-->
                    <Setter Property="BorderThickness" Value="1"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel>
        <TextBox Margin="5"/>
        <TextBox Margin="5,0"/>
        <TextBox Margin="5"/>
    </StackPanel>
</Window>

这个例子中唯一需要解释的就是DataTrigger的Binding。为了将控件自己作为数据源,我
们使用了RelativeSource,初学者经常认为“不明确指出Source的值Binding就会将控件自
己作为数据的来源”,这是错误的,因为不名曲指出Source时Binding会把控件的
DataContext属性作为数据源而非把控件自身当作数据源。Binding的Path被设置为
Text.Length,即我们关注的是字符串的长度。长度是一个具体的数字,如何基于这个长度
值做判断呢?这就用到了Converter。我们创建如下的Converter:

using System; 
using System.Globalization; 
using System.Windows.Data; 
namespace Converter 
{ 
public class L2BConverter : IValueConverter 
{ 
public object Convert(object value, Type targetType, object 
parameter, CultureInfo culture) 
{ 
//获取输入值并强制转换 
int textLength = (int)value; 
//三目运算符:判断数值长度是否>6 ? 是返回true : 否返回false 
return textLength > 6 ? true : false; 
} 
public object ConvertBack(object value, Type targetType, object 
parameter, CultureInfo culture) 
{ 
throw new NotImplementedException(); 
} 
} 
} 

经Converter转换后,长度值会转换成bool类型值。DataTrigger的Value被设置为false,
也就是说当TextBox的文本长度小于7时DataTrigger会使用自己的一组Setter把TextBox的边框设置为红色。运行效果如图所示:
在这里插入图片描述

第四种,多数据条件触发的MultiDataTrigger
有时我们会遇到要求多个数据条件同时满足时才能触发变化的需求,此时可以考虑使
用MultiDataTrigger。比如有这样一个需求:用户界面上使用ListBox显示了一列Student
数据,当Student对象同时满足ID为2、Name为Tom的时候,条目的高亮显示。

<Window x:Class="WpfApplication1.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="clr-namespace:WpfApplication1" 
Title="MultiDataTrigger" Height="146" Width="300" 
Loaded="Window_Loaded"> 
<Window.Resources>
        <Style TargetType="ListBoxItem">
            <!--使用Style设置 DataTemplate-->
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding ID}" Width="60"/>
                            <TextBlock Text="{Binding Name}" Width="120"/>
                            <TextBlock Text="{Binding Age}" Width="60"/>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <!--多数据条件触发的MultiDataTrigger-->
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=ID}" Value="2"/>
                        <Condition Binding="{Binding Path=Name}" Value="Tom"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Background" Value="Orange"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
                <!--多数据条件触发的MultiDataTrigger-->
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding Path=ID}" Value="4"/>
                        <Condition Binding="{Binding Path=Age}" Value="21"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Background" Value="SkyBlue"/>
                    </MultiDataTrigger.Setters>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel>
        <ListBox x:Name="listBoxStudent" Margin="5"/>
    </StackPanel>
</Window> 

示例的C#代码部分包括声明带有ID、Name、Age属性的Student 类和将一个
List实例赋值给ListBox的ItemsSource属性

using System.Collections.Generic; 
using System.Windows; 
namespace WpfApplication1 
{ 
/// <summary> 
/// MainWindow.xaml 的交互逻辑 
/// </summary> 
public partial class Window1 : Window 
{ 
public Window1() 
{ 
InitializeComponent(); 
} 
public class Student 
{ 
public int ID { get; set; } 
public string Name { get; set; } 
public string Age { get; set; } 
} 
private void Window_Loaded(object sender, RoutedEventArgs e) 
{ 
List<Student> sss = new List<Student>() { 
new Student() { ID = 1, Name = "Tim", Age="16" }, 
new Student() { ID = 2, Name = "Tom", Age="18" }, 
new Student() { ID = 3, Name = "Yue", Age="20" }, 
new Student() { ID = 4, Name = "Kenny", Age="21" }, 
new Student() { ID = 5, Name = "Yue", Age="22" }, 
}; 
listBoxStudent.ItemsSource = sss; 
} 
} 
} 

程序运行效果如下:
在这里插入图片描述

第五种,由事件触发的EventTrigger
EventTrigger是触发器中最特殊的一个。首先,它不是由属性值或数据的变化来触发
而由事件来触发;其次,被触发后它并非应用一组Setter,而是执行一段动画。因此,UI
层的动画效果往往与 EventTrigger事件触发,另一个由MouseLeave事件触发。XAML代
码如下:

<Window x:Class="测试后面删除.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="clr-namespace:测试后面删除" 
Title="Window1" Height="240" Width="240"> 
<Window.Resources>
        <Style TargetType="Button">
            <Style.Triggers>
                <!--鼠标进入-->
                <EventTrigger RoutedEvent="MouseEnter">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Width"/>
                            <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Height"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <!--鼠标离开-->
                <EventTrigger RoutedEvent="MouseLeave">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Width"/>
                            <DoubleAnimation To="150" Duration="0:0:0.2" Storyboard.TargetProperty="Height"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Canvas>
        <Button Width="40" Height="40" Content="OK"/>
    </Canvas>
</Window> 

无需要任何C#代码,我们就获得了效果(界面上一些可展开/收拢的工具箱或菜单就是使
用这种EventTrigger制作的)
至此各种触发器就介绍完了,提醒大家注意一点:虽然在Style中大量使用触发器,但是
触发器并非只能应用在Style中——各种Template也可以拥有自己的触发器,请大家根据
设计需要决定触发器放在Style中还是Template中

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值