WPF触发器的分类及使用
触发器简介
在WPF中,是这样描述触发器的,在属性改变或触发事件时设置属性或启动操作。意思很明了,满足两个条件中任意一个:1、属性改变 2、某一个事件触发,结果就是上面条件发生了,就设置其它的一些属性,或者能启动某个动作。这就是WPF中触发器的作用。在WPF中,触发器按触发的类别可以分为三类,即属性触发器、事件触发器、数据触发器,按触发条件的数量可以划分为:多条件触发器、单条件触发器。下面将按类别进行讲解。
属性触发器(PropertyTrigger)
在属性值发生更改时触发属性的设置或启动操作的Trigger称为属性触发器。如下代码设置了ListBoxItem的Opacity属性为0.5,也即是半透明。当ListBoxItem的IsSelected属性发生了改变,值变为True时,触发了其属性Opacity被设置为1.0,也即是不透明,示例代码如下:
<Window.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Opacity" Value="0.5" />
<Setter Property="Height" Value="35" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Trigger.Setters>
<Setter Property="Opacity" Value="1.0" />
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox>
<ListBoxItem Content="测试1"/>
<ListBoxItem Content="测试2"/>
<ListBoxItem Content="测试3"/>
</ListBox>
</Grid>
事件触发器(EventTrigger)
在事件发生更改时触发属性的设置或启动一组操作的Trigger称为事件触发器。下面的代码将展示ListBoxItem默认Height属性为35,当鼠标移动弄到ListBoxItem上面时,触发从0.2秒内Height属性值从35到100的动画变化。当鼠标移出ListBoxItem时,其Height将从100又动画变化到35,示例代码如下:
<Window.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Opacity" Value="1.0" />
<Setter Property="Height" Value="35" />
<Style.Triggers>
<EventTrigger RoutedEvent="Mouse.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:0.2"
Storyboard.TargetProperty="Height"
To="100" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Duration="0:0:1"
Storyboard.TargetProperty="Height" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox>
<ListBoxItem Content="测试1"/>
<ListBoxItem Content="测试2"/>
<ListBoxItem Content="测试3"/>
</ListBox>
</Grid>
数据触发器(DataTrigger)
当给定的数据满足指定的条件时执行设置属性或执行给定的操作。数据触发器与上面两种触发器的本质区别在于条件数据来源于用户自定义的数据,而不是控件元素属性值。下面的代码展示了给定ListBox的数据源为Book的列表,当Book类对象成员Stock值为out时,触发的对象为活动时,在1秒内重复Opacity从0.25到0.5的透明度变化,触发的对象为非活动时,1秒内Opacity变为1.0,示例代码如下:
<Window.Resources>
<XmlDataProvider x:Key="InventoryData" XPath="Inventory/Books">
<x:XData>
<Inventory xmlns="">
<Books>
<Book ISBN="0-7356-0562-9" Stock="in" Number="9">
<Title>XML in Action</Title>
<Summary>XML Web Technology</Summary>
</Book>
<Book ISBN="0-7356-1370-2" Stock="in" Number="8">
<Title>Programming Microsoft Windows With C#</Title>
<Summary>C# Programming using the .NET Framework</Summary>
</Book>
<Book ISBN="0-7356-1288-9" Stock="out" Number="7">
<Title>Inside C#</Title>
<Summary>C# Language Programming</Summary>
</Book>
<Book ISBN="0-7356-1377-X" Stock="in" Number="5">
<Title>Introducing Microsoft .NET</Title>
<Summary>Overview of .NET Technology</Summary>
</Book>
<Book ISBN="0-7356-1448-2" Stock="out" Number="4">
<Title>Microsoft C# Language Specifications</Title>
<Summary>The C# language definition</Summary>
</Book>
</Books>
<CDs>
<CD Stock="in" Number="3">
<Title>Classical Collection</Title>
<Summary>Classical Music</Summary>
</CD>
<CD Stock="out" Number="9">
<Title>Jazz Collection</Title>
<Summary>Jazz Music</Summary>
</CD>
</CDs>
</Inventory>
</x:XData>
</XmlDataProvider>
<Style x:Key="AnimatedListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Margin" Value="0,2,0,2" />
<Setter Property="Padding" Value="0,2,0,2" />
<Style.Triggers>
<DataTrigger
Binding="{Binding XPath=@Stock}"
Value="out">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
From="0.25" To="0.5" Duration="0:0:1"
RepeatBehavior="Forever"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard FillBehavior="Stop">
<DoubleAnimation
Storyboard.TargetProperty="Opacity"
To="1" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<ListBox HorizontalAlignment="Center"
ItemContainerStyle="{StaticResource AnimatedListBoxItemStyle}"
Padding="2">
<ListBox.ItemsSource>
<Binding Source="{StaticResource InventoryData}"
XPath="*"/>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="12" Margin="0,0,10,0">
<TextBlock.Text>
<Binding XPath="Title"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
多条件触发器(MultiTrigger)
当属性条件有多个,并且多个条件都成立的条件下,触发的Trigger称为多条件属性触发器,多条件触发器触发后,会设置属性或执行给定的操作。以下代码实现了当ListBoxItem的IsSelected和IsEnabled都设置为True时,其Foreground被设置为红色(注意:这里没有用Background的原因是Background是用来显示控件的背景的,因此直接设置没有任何反应,如需要改变背景颜色,则需要在Template里去实现触发),触发时被设置的属性需要有一个默认设置,这里的Foreground默认设置为Black,示例代码如下:
<Window.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Opacity" Value="1.0" />
<Setter Property="Height" Value="35" />
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsEnabled" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="Red"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ListBox>
<ListBoxItem Content="测试1"/>
<ListBoxItem Content="测试2" IsEnabled="False"/>
<ListBoxItem Content="测试3"/>
</ListBox>
</Grid>
当数据条件有多个,并且多个条件都成立的条件下,触发的Trigger称为多条件数据触发器,多条件触发器触发后,会设置属性或执行给定的操作。以下代码实现了当给定的数据的成员Stock为"in",Number成员为8时,其触发的控件的前景色(Foreground)为红色(Red),示例代码如下:
<Window.Resources>
<XmlDataProvider x:Key="InventoryData" XPath="Inventory/Books">
<x:XData>
<Inventory xmlns="">
<Books>
<Book ISBN="0-7356-0562-9" Stock="in" Number="9">
<Title>XML in Action</Title>
<Summary>XML Web Technology</Summary>
</Book>
<Book ISBN="0-7356-1370-2" Stock="in" Number="8">
<Title>Programming Microsoft Windows With C#</Title>
<Summary>C# Programming using the .NET Framework</Summary>
</Book>
<Book ISBN="0-7356-1288-9" Stock="out" Number="7">
<Title>Inside C#</Title>
<Summary>C# Language Programming</Summary>
</Book>
<Book ISBN="0-7356-1377-X" Stock="in" Number="5">
<Title>Introducing Microsoft .NET</Title>
<Summary>Overview of .NET Technology</Summary>
</Book>
<Book ISBN="0-7356-1448-2" Stock="out" Number="4">
<Title>Microsoft C# Language Specifications</Title>
<Summary>The C# language definition</Summary>
</Book>
</Books>
<CDs>
<CD Stock="in" Number="3">
<Title>Classical Collection</Title>
<Summary>Classical Music</Summary>
</CD>
<CD Stock="out" Number="9">
<Title>Jazz Collection</Title>
<Summary>Jazz Music</Summary>
</CD>
</CDs>
</Inventory>
</x:XData>
</XmlDataProvider>
<Style x:Key="AnimatedListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Margin" Value="0,2,0,2" />
<Setter Property="Padding" Value="0,2,0,2" />
<Setter Property="Height" Value="35"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding XPath=@Number}" Value="8"/>
<Condition Binding="{Binding XPath=@Stock}" Value="in"/>
</MultiDataTrigger.Conditions>
<Setter Property="Foreground" Value="Red"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<ListBox HorizontalAlignment="Center"
ItemContainerStyle="{StaticResource AnimatedListBoxItemStyle}"
Padding="2">
<ListBox.ItemsSource>
<Binding Source="{StaticResource InventoryData}"
XPath="*"/>
</ListBox.ItemsSource>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="12" Margin="0,0,10,0">
<TextBlock.Text>
<Binding XPath="Title"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
视觉状态(补充说明)
在WPF中,控件有六种状态,共分为两组,即CommonStates组和FocusStates组,具体细节将在下图体现。其运作方式是System.Windows.VisualStateManager进入特定状态时发动动画,VisualStateManager 声明要监视的 VisualStateGroup 和 VisualState 的组合。六种状态信息如下:
VisualState名称 | VisualStateGroup名称 | 描述 |
---|---|---|
Normal | CommonStates | 默认状态。 |
MouseOver | CommonStates | 鼠标指针悬停在控件上方。 |
Pressed | CommonStates | 已按下控件。 |
Disabled | CommonStates | 已禁用控件。 |
Focused | FocusStates | 控件有焦点。 |
Unfocused | FocusStates | 控件没有焦点。 |
XAML示例代码如下:
<Rectangle Name="rect"
Width="100" Height="100"
MouseEnter="rect_MouseEvent"
MouseLeave="rect_MouseEvent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="MouseStates">
<VisualState Name="MouseEnter">
<Storyboard>
<ColorAnimation To="Green"
Storyboard.TargetName="rectBrush"
Storyboard.TargetProperty="Color"/>
</Storyboard>
</VisualState>
<VisualState Name="MouseLeave" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle.Fill>
<SolidColorBrush x:Name="rectBrush" Color="Red"/>
</Rectangle.Fill>
</Rectangle>
后端示例代码如下:
private void rect_MouseEvent(object sender, MouseEventArgs e)
{
if (rect.IsMouseOver)
{
VisualStateManager.GoToElementState(rect, "MouseEnter", true);
}
else
{
VisualStateManager.GoToElementState(rect, "MouseLeave", true);
}
}
EnterActions和ExitActions 简介(补充说明)
这两个属性是Trigger类中可以用于除了事件触发器以外触发器,其目的是用来使触发器执行操作,比较常用的是进行控件动画。EnterActions和ExitActions都是由TriggerActionCollection定义而成,而TriggerActionCollection是实现TriggerAction类列表的。因此,需要执行操作的类必须是继承TriggerAction的。而在WPF动画处理中,比较常规的处理动画的办法是用BeginStoryboard启动一个动画操作,因此毫无疑问BeginStoryboard类就是TriggerAction的子类。至于动画的一些其它细节,将在后续文章中再做具体分析。