欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WPF中Style样式及其触发器

程序员文章站 2022-07-13 22:54:25
...
开发工具与关键技术:WPF C#
作者:黄济民
撰写时间:2021年10月15日

1、Setter,设置器。什么的设置器呢?属性值的。我们给属性赋值的时候一般都采用“属性名=属性值”的形式。Setter类的Property属性用来指明你想为目标的那个属性赋值;Setter类的Value属性则是你提供的属性值。
2、下面的例子中在Window的资源词典中放置一个针对TextBlock的Style,Style中使用若干Setter来设定TextBlock的一些属性,这样程序中的TextBlock就会具有统一的风格,除非你使用{x:Null}显示地清空Style
XAML代码如下:

<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>

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

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

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

<Window.Resources>
        <!--复选框CheckBox样式-->
        <Style TargetType="CheckBox">
            <Style.Triggers>
                <!--IsChecked选中改变字体大小为20,字体样式为橙色Orange-->
                <Trigger Property="IsChecked" Value="true">
                    <Trigger.Setters>
                        <Setter Property="FontSize" Value="20"/>
                        <Setter Property="Foreground" Value="Orange"/>
                    </Trigger.Setters>
                </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>

因为Triggers不是Style的内容属性,所以 <Style.Triggers>… </Style.Triggers>这层标签
不能省略,但Trigger是Trigger的内容属性,所以<Trigger.Setters>…<Trigger.Setters>这
层标签是可以省略的,以上代码可以简化为:

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

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

<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>

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

using System;
using System.Globalization;
using System.Windows.Data;
namespace MyWPF
{
    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();
        }
    }
}

3、经Converter转换后,长度值会转换成bool类型值。DataTrigger的Value被设置为false,也就是说当TextBox的文本长度小于7时DataTrigger会使用自己的一组Setter把TextBox的边框设置为红色。

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

<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>

然后在 Title=“QQ” Height=“400” Width=“400” Loaded=“Window_Loaded”>事件C#如下:

        /// <summary>
        /// 学生实体
        /// </summary>
        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;
        }

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

<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>

注意:虽然在Style中大量使用触发器,但是触发器并非只能应用在Style中——各种Template也可以拥有自己的触发器,请大家根据设计需要决定触发器放在Style中还是Template中。

相关标签: C# wpf microsoft