1111购物狂欢节,杭州拱墅区教育局,圣杯骑士
VisualTransition是控件模板中的重要组成部分,无论是自定义控件或者修改控件样式都会接触到VisualTransition。明明这么重要,博客园上好像都没多少关于VisualTransition的主题。
VisualTransition动画定义VisualState之前切换时的过渡行为,包括过渡时间和过渡动画。
VisualTransition的类定义如下:
[ContentProperty(Name = "Storyboard")] public class VisualTransition : DependencyObject, IVisualTransition { public VisualTransition(); // 摘要: // 获取或设置要转换为的 Windows.UI.Xaml.VisualState 的名称。 public string To { get; set; } // // 摘要: // 获取或设置在发生转换时运行的 Windows.UI.Xaml.Media.Animation.Storyboard。 public Storyboard Storyboard { get; set; } // // 摘要: // 获取或设置应用于生成的动画的缓动函数。 public EasingFunctionBase GeneratedEasingFunction { get; set; } // // 摘要: // 获取或设置从一种状态转换到另一种状态所花的时间,以及任何隐式过渡动画应作为过渡行为的一部分运行的时间 public Duration GeneratedDuration { get; set; } // // 摘要: // 获取或设置要转换的 Windows.UI.Xaml.VisualState 的名称。 public string From { get; set; } }
虽然自WPF4以来VisualTransition一直都存在,但很多人还是习惯这样写VisualState:
<VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PointOverElement" Duration="0" To="1" /> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PressElement" Duration="0" To="1" /> </Storyboard> </VisualState> <VisualState x:Name="Disabled" /> </VisualStateGroup>
正确的做法应该是这样:
<VisualStateGroup x:Name="CommonStates"> <VisualStateGroup.Transitions> <VisualTransition To="PointerOver"> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PointOverElement"> <EasingDoubleKeyFrame KeyTime="0" Value="0" /> <EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <CubicEase EasingMode="EaseOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="Pressed"> <Storyboard> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PressElement"> <EasingDoubleKeyFrame KeyTime="0" Value="0" /> <EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <CubicEase EasingMode="EaseOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="Disabled"> <Storyboard Completed="Storyboard_Completed"></Storyboard> </VisualTransition> </VisualStateGroup.Transitions> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PointOverElement" Duration="0" To="1" /> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Opacity" Storyboard.TargetName="PressElement" Duration="0" To="1" /> </Storyboard> </VisualState> <VisualState x:Name="Disabled" /> </VisualStateGroup>
可以看到VisualState中的Storyboard只用于定义VisualState的最终可视状态,而在VIsualState间转换时用户看到的是VisualTransition 中定义的Storyboard。但这样的话两处的Storyboard不就重复了?带着这个疑问很多年,微软终于给出了另一种方案VisualState.Setters:
<VisualStateGroup x:Name="CommonStates"> <VisualStateGroup.Transitions> ... </VisualStateGroup.Transitions> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <VisualState.Setters> <Setter Target="PointOverElement.(UIElement.Opacity)" Value="1" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Pressed"> <VisualState.Setters> <Setter Target="PressElement.(UIElement.Opacity)" Value="1" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled" /> </VisualStateGroup>
这样VisualState的做法就十分清晰明了:
不使用Storyboard的VisualTransition称为隐式转换:
<VisualStateGroup.Transitions > <VisualTransition GeneratedDuration="0:0:3"/> </VisualStateGroup.Transitions>
如上面这段XAML中的VisualTransition ,它指定VisualStateGroup中所有VisualState之间的过渡时间都是3秒,在这3秒中VisualState中的Double、Point和Color使用默认的线性插值方式进行动画转换。而其它值,如Visibility,则不可以使用隐式转换。
这段XAML在Blend中对应“状态”面板里VisualStateGroup的“默认过渡”。
隐式转换可以进一步设置其它属性,如以下XAML:
<VisualStateGroup.Transitions> <VisualTransition To="PointerOver" GeneratedDuration="0:0:3"> <VisualTransition.GeneratedEasingFunction> <ExponentialEase EasingMode="EaseOut" /> </VisualTransition.GeneratedEasingFunction> </VisualTransition> <VisualTransition From="PointerOver" To="Pressed" GeneratedDuration="0:0:3"> <VisualTransition.GeneratedEasingFunction> <ExponentialEase EasingMode="EaseOut" /> </VisualTransition.GeneratedEasingFunction> </VisualTransition> </VisualStateGroup.Transitions>
这段XAML中VisualTransition指定了以下三种属性:
当隐式转换不能满足需求,可以使用Storyboard指定转换的动画。这时Storyboard不需要设置FillBehavior="HoldEnd"
,因为Storyboard结束后将保持VisualState设置的最终状态。
<VisualTransition To="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PointOverElement"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PointOverElement"> <EasingDoubleKeyFrame KeyTime="0" Value="0" /> <EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <CubicEase EasingMode="EaseOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PressElement"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PressElement"> <EasingDoubleKeyFrame KeyTime="0" Value="0" /> <EasingDoubleKeyFrame KeyTime="0:0:2" Value="1"> <EasingDoubleKeyFrame.EasingFunction> <CubicEase EasingMode="EaseOut" /> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition>
ControlTemplate在VisualState之间切换是靠下面这个函数控制的:
// // 摘要: // 通过按名称请求新的 Windows.UI.Xaml.VisualState 来在两个状态之间转换控件。 // // 参数: // control: // 要进行状态过渡的控件。 // // stateName: // 要过渡到的状态。 // // useTransitions: // 如果使用 Windows.UI.Xaml.VisualTransition 在各状态之间转换,则为 **true**。 如果跳过使用转换并直接转到请求的状态,则为 // **false**。 默认值为 **false**。 // // 返回结果: // 如果控件成功转换到新状态或者已经在使用该状态,则为 **true**;否则为 **false**。 public static bool GoToState(Control control, string stateName, bool useTransitions);
如果useTransitions这个参数为false,则VisualState之间切换时不会使用VisualTransition。在控件加载模板时(即调用OnApplyTemplate()函数时)通常会这样做,因为控件在呈现时通常都不需要做动画。
另外,VisualStateManager.GoToState不会使控件重复进入某个状态,即如果控件已处于PointerOver的VisualState,再次调用VisualStateManager.GoToState(this, PointerOverState, useTransitions)不会触发任何操作,也不会重复触发动画。
除了VisualState.Setters,这篇文章的内容基本和WPF通用。
上次被批评写得太复杂了,这次本来写了很多,为了文章简单易懂删了一半,希望对理解VisualTransition有帮助。
VisualTransition Class (Windows)
VisualTransition Class (Windows.UI.Xaml) - UWP app developer Microsoft Docs
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
Net Core Web Api项目与在NginX下发布的方法
asp.net core3.1 引用的元包dll版本兼容性问题解决方案
IdentityServer4实现.Net Core API接口权限认证(快速入门)
ASP.NET Core MVC通过IViewLocationExpander扩展视图搜索路径的实现
网友评论