本章前面的"控件模板"中,介绍了如何创建控件模板,自定义控件的外观。其中还缺了些什么,使用按钮的默认模板,按钮会响应鼠标的移动和单击,当鼠标移动到按钮或单击按钮时,按钮的外观是不同的。这种外观变化通过可视化状态和动画来处理,由可视化状态管理器(VisualStateManager)控制。
本节介绍如何改变按钮样式,来响应鼠标的移动和单击,还描述了如何创建自定义状态,当几个控件应该切换到禁用状态时,例如进行一些后台处理时,这些自定义状态用于处理完整页面的变化。
对于XAML元素,可以定义可视化状态、状态组、和状态,指定状态的特定动画。状态组允许同时有多个状态。对于一组,一次只能有一个状态。然而,另一组的另一个状态可以在同一时间激活。例如,按钮的状态和状态组。按钮控件定义了状态组CommonStates和FocusStates。用FocusStates定义的状态是Focused、UnFocused和PointerFocused,CommonStates组定义了状态Normal、PointerOver、Pressed和Disabled。有了这些选项,多个状态可以同时激活,但一个状态组内总是只有一个状态是激活的。例如,按钮可以是Focused和Normal状态。它也可以是Focused和Pressed状态,还可以定义定制的状态和状态组。
下面看看具体的例子。
1. 用控件模板预定义状态
下图利用先前创建的自定义控件模板,样式化按钮控件,使用可视化状态改进它。为此,一个简单的方法是使用Microsoft Blend for Visual Studio,下图显示了状态窗口,选择控件模板时就会显示该窗口。在这里可以看到控件的可用状态,并基于这些状态记录变化。
之前的按钮模板改为可视化状态:Pressed、Disabled和PointerOver。在状态中,Storyboard定义了一个ColorAnimation来改变椭圆的Fill属性的颜色:
<Page.Resources>
<Style x:Key="RoundedGelButton" TargetType="Button">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Duration="0" To="#FFC8CE11"
Storyboard.TargetName="GelBackground"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
d:IsOptimized="true"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ColorAnimation Duration="0" To="#FF606066"
Storyboard.TargetName="GelBackground"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
d:IsOptimized="True"/>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation Duration="0" To="#FF0F9D3A"
Storyboard.TargetName="GelBackground"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="GelBackground" StrokeThickness="0.5" Fill="Black">
<Ellipse.Stroke>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#ff7e7e7e"/>
<GradientStop Offset="1" Color="Black"/>
</LinearGradientBrush>
</Ellipse.Stroke>
</Ellipse>
<Ellipse Margin="15,5,15,50">
<Ellipse.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#aaffffff"/>
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter x:Name="GelButtonContent" VerticalAlignment="Center" HorizontalAlignment="Center"
Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
现在运行应用程序,可以看到颜色随着鼠标的移动和单击而变化。
2. 定义自定义状态
使用VisualStateManager可以定义定制的状态,使用VisualStateGroup和VisualState的状态可以定义定制的状态组。下面的代码片段在名为CustomStates的组内创建了Enabled和Disabled状态。该可视化状态管理器在主窗口的网格中定义。改变状态时,Button元素的IsEnabled属性使用DiscreteObjectKeyFrame动画立即改变:
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CustomStates">
<VisualState x:Name="Enabled"/>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="button1"
Storyboard.TargetProperty="Control.IsEnabled">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<x:Boolean>False</x:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="button2"
Storyboard.TargetProperty="Control.IsEnabled">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<x:Boolean>False</x:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Spacing="10">
<Button Content="Click Me!" Style="{StaticResource RoundedGelButton}"/>
<Button x:Name="button1" Content="OnEnable" Click="OnEnable"/>
<Button x:Name="button2" Content="OnDisable" Click="OnDisable"/>
</StackPanel>
</Grid>
3. 设置自定义的状态
现在需要设置状态。为此,可以调用VisualStateManager类的GoToState方法。在代码隐藏文件中,OnEnabled和OnDisabled方法是页面上两个按钮的Click事件处理程序:
private void OnEnable(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(this,"Enabled",useTransitions:true);
}
private void OnDisable(object sender, RoutedEventArgs e)
{
VisualStateManager.GoToState(this,"Disabled",useTransitions:true);
}
在真实的应用程序中,可以以类似的方式更改状态,例如执行网络调用时,用户不应该处理页面内的一些控件。用户仍应被允许单击取消按钮。通过改变状态,还可以显示进度信息。