https://docs.microsoft.com/zh-cn/dotnet/api/xamarin.forms.animation?view=xamarin-forms
要使用 VisualStateManager,需要定义 VisualState;在 VisualState 中定义控件的不同的状态以及每种状态下的样式,然后,在代码中合适的地方,我们可以使用 VisusalStateManager 类的 GoToState 来切换到对应的状态,从而实现样式的切换。
- VisualState: 视图状态(Visual States)表示控件在一个特殊的逻辑状态下的样式、外观;
- VisualStateGroup: 状态组由相互排斥的状态组成,状态组与状态组并不互斥;
- VisualTransition: 视图转变 (Visual Transitions) 代表控件从一个视图状态向另一个状态转换时的过渡;
- VisualStateManager: 由它负责在代码中来切换到不同的状态;
每个 VisualState 都属于一个状态组 (VisualStateGroup),也即一个 VisualStateGroup 中可以定义多个 VisualState;并且,我们也可以定义多个 VisualStateGroup;需要再次强调的是:同一个 VisualStateGroup 中 VisualState 是互斥的,而不同的 VisualStateGroup 中的 VisualState 是在同一时刻是可以共存的。以 Button 为例:
我们看到,在它里面,定义了三个 VisualStateGroup,分别是 CommonStates(正常状态)、FocusStates(焦点状态)、ValidationStates(验证状态),而每个 VisualStateGroup 下又有若干个 VisualState。在 CommonStates 中,按钮可以是 Normal 、MouseOver 或 Pressed(只能是三者之一),但它却可以结合其它 VisualStateGroup 中的 VisualState 来显示,如按钮具有焦点时且鼠标移动到其上,这就结合了 MouseOver 与 Focused 两种状态。以下它的部分代码:
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1" To="MouseOver" />
<VisualTransition GeneratedDuration="0:0:1" To="Pressed" />
<VisualTransition GeneratedDuration="0:0:1" To="Normal" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="BackgroundBorder"
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#A1D6FC"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation
Storyboard.TargetName="BackgroundBorder"
Storyboard.TargetProperty="Background.(SolidColorBrush.Color)"
To="#FCA1A1"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border
x:Name="BackgroundBorder"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true" />
<ContentPresenter
Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ControlTemplate>
在自定义控件的开发过程中,我们也可以采用同样的原则,即在 XAML 中定义 VisualStateGroup、VisualState 以及 VisualTransition(可选),由借助于 VisualStateManager 来实现切换。以下是一个带水印功能的 TextBox 中 VisualStates 的定义:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="WatermarkGroup">
<VisualStateGroup.Transitions>
<VisualTransition From="ShowWatermarkState" To="HideWatermarkState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_Watermark"
Storyboard.TargetProperty="Opacity" From="1"
To="0" Duration="0:0:2" />
</Storyboard>
</VisualTransition>
<VisualTransition From="HideWatermarkState" To="ShowWatermarkState">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_Watermark"
Storyboard.TargetProperty="Opacity" From="0"
To="1" Duration="0:0:2" />
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="ShowWatermarkState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0:0:0"
Storyboard.TargetName="PART_Watermark"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="HideWatermarkState">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0:0:0"
Storyboard.TargetName="PART_Watermark"
Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Collapsed}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
可以看出,它定义了 WatermarkGroup 组,并在其中定义了 ShowWatermarkState 和 HideWatermarkState 两个 VisualState,并且,通过定义的 VisualTransition 能够实现在这两种状态下切换时,水印文本会有淡入、淡出的效果。
最后,在代码中,调用 VisualStateManager.GoToState 方法来切换到合适的状态即可:
private void UpdateState()
{
bool textExists = Text.Length > 0;
var watermark = GetTemplateChild("PART_Watermark") as FrameworkElement;
var state = textExists || IsFocused ? "HideWatermarkState" : "ShowWatermarkState";
VisualStateManager.GoToState(this, state, true);
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
UpdateState();
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
UpdateState();
}
https://blog.csdn.net/weixin_44138053/article/details/86523833
ContentPresenter 模板化视图的布局管理器。
Content | 获取或设置使用 ContentPresenter 管理其布局的视图。 |
QuarticEase 一个缓动函数,该函数使用公式 f(t) = t4 创建加速和/或减速的动画。
<Rectangle Name="myRectangle" Width="200" Height="30" Fill="Blue">
<Rectangle.Triggers>
<EventTrigger RoutedEvent="Rectangle.MouseDown">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="30" To="200" Duration="00:00:3"
Storyboard.TargetName="myRectangle"
Storyboard.TargetProperty="Height">
<DoubleAnimation.EasingFunction>
<QuarticEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Rectangle.Triggers>
</Rectangle>
Animation 封装动画,此函数集合在用户可察觉的时间段内修改属性。
DoubleAnimation类创建两个目标值之间的转换。 若要设置其目标值,使用其From, To,和By属性。 下表总结了如何From, To,和By属性可能会一起使用或单独来确定动画的目标值。
指定的属性 | 产生的行为 |
---|---|
From | 从指定的值的动画From属性进行动画处理的属性的基值或前一个动画的输出值,具体取决于前一个动画的配置方式。 |
From 和 To | 从指定的值的动画From属性设置为指定的值To属性。 |
From 和 By | 从指定的值的动画From之和指定的值的属性From和By属性。 |
To | 动画继续处理从经过动画处理的属性的基值或前一个动画的输出值与指定的值To属性。 |
By | 动画从要进行动画处理的属性的基值或前一个动画的输出值和指定的值的总和值By属性。 |
EasingFunction 获取或设置应用于此动画的缓动函数。
Duration | 获取或设置此时间线播放的时间长度,而不是计数重复。(Inherited from Timeline) |
EasingFunction | 获取或设置应用于此动画的缓动函数。 |
Storyboard.TargetName 获取或设置要进行动画处理的对象的名称。
注解
设置此属性是可选的。 如果TargetName未指定,情节提要的动画应用于下列情况之一:
-
如果使用启动情节提要动画所属BeginStoryboard,拥有的元素BeginStoryboard触发情节提要的操作为目标。
-
如果使用启动情节提要Begin方法,FrameworkElement或FrameworkContentElement指定当启动情节提要时采用了Begin方法为目标。
如果使用子时间线上设置此属性,这些子时间线"继承"的父TargetName除非指定其自己。
使对象作为目标
当使用XAML,执行以下两个操作,以使对象可通过一个情节提要之一:
-
如果对象是FrameworkElement或FrameworkContentElement,将其Name属性。
-
如果对象是Freezable或自定义FrameworkContentElement或FrameworkContentElement,将其分配名称使用X:name 指令标记扩展。
使用代码时,您使对象可通过使用RegisterName方法以将对象分配一个名称。
依赖项属性信息
标识符字段 | TargetNameProperty |
元数据属性设置为 true | 无 |
Storyboard.TargetProperty 标识 Target 附加属性。
https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.dependencyproperty?view=netframework-4.8
DependencyProperty
表示可通过诸如样式、数据绑定、动画和继承等方法设置的属性。