如何通过后台代码手动触发页面刷新动画?NavigationView切换Frame动画问题
嘿,这个场景我之前在做多模块导航应用的时候也踩过坑!你已经给每个Frame配置了导航栈内的页面切换动画,但切换激活Frame本身时没有过渡效果,其实可以通过几种方式手动触发,下面给你拆解几个实用的方案:
方案1:复用Frame内置的导航过渡动画
如果你给每个Frame配置的是系统内置的NavigationTransitionInfo(比如DrillInNavigationTransitionInfo、SlideNavigationTransitionInfo),那可以用一个“伪导航”的小技巧——让目标Frame重新导航到它当前显示的页面,同时指定过渡效果,这样就能直接复用你已经配置好的动画,还不会破坏页面的状态:
// 获取要切换到的目标Frame var targetFrame = YourNavigationView.SelectedItem.Tag as Frame; // 获取Frame当前显示的页面 var currentPage = targetFrame.Content as Page; if (currentPage != null) { // 导航回当前页面,传入你之前配置的过渡动画类型 targetFrame.Navigate( currentPage.GetType(), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight } ); }
注意:要确保你的Frame的CacheMode设置为Required或者Enabled,这样页面不会被重建,之前的操作状态就能完美保留。
方案2:用VisualStateManager自定义Frame切换动画
如果想要更灵活的动画效果(比如淡入淡出+平移组合),可以给每个Frame定义对应的激活/非激活视觉状态,然后在切换时手动触发状态切换:
首先在你的布局XAML里给Frame添加视觉状态和动画:
<Grid> <!-- 假设你的多个Frame都放在这个Grid里,默认只有激活的Frame可见 --> <Frame x:Name="HomeFrame" RenderTransformOrigin="0.5,0.5"> <Frame.RenderTransform> <TranslateTransform X="0"/> </Frame.RenderTransform> </Frame> <Frame x:Name="SettingsFrame" Opacity="0" RenderTransformOrigin="0.5,0.5"> <Frame.RenderTransform> <TranslateTransform X="20"/> </Frame.RenderTransform> </Frame> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="FrameActivationStates"> <!-- 主页Frame激活状态 --> <VisualState x:Name="HomeActive"> <Storyboard> <DoubleAnimation Storyboard.TargetName="HomeFrame" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3"/> <DoubleAnimation Storyboard.TargetName="HomeFrame" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" To="0" Duration="0:0:0.3"/> <DoubleAnimation Storyboard.TargetName="SettingsFrame" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.3"/> <DoubleAnimation Storyboard.TargetName="SettingsFrame" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" To="20" Duration="0:0:0.3"/> </Storyboard> </VisualState> <!-- 设置页Frame激活状态 --> <VisualState x:Name="SettingsActive"> <Storyboard> <DoubleAnimation Storyboard.TargetName="SettingsFrame" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3"/> <DoubleAnimation Storyboard.TargetName="SettingsFrame" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" To="0" Duration="0:0:0.3"/> <DoubleAnimation Storyboard.TargetName="HomeFrame" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.3"/> <DoubleAnimation Storyboard.TargetName="HomeFrame" Storyboard.TargetProperty="RenderTransform.(TranslateTransform.X)" To="-20" Duration="0:0:0.3"/> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid>
然后在后台切换Frame的代码里,触发对应的视觉状态:
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { var selectedItem = args.SelectedItem as NavigationViewItem; string targetState = selectedItem.Tag.ToString(); // 假设Tag存的是视觉状态名,比如"HomeActive" // 触发视觉状态切换,第三个参数true表示启用动画 VisualStateManager.GoToState(this, targetState, true); }
方案3:手动控制Storyboard播放
如果不想用VisualState,也可以直接创建Storyboard实例,在切换Frame的时候手动启动动画:
private void SwitchFrameWithAnimation(Frame oldFrame, Frame newFrame) { // 创建退出动画:旧Frame淡出+向左平移 var exitStoryboard = new Storyboard(); var exitOpacityAnim = new DoubleAnimation { From = 1, To = 0, Duration = TimeSpan.FromMilliseconds(300) }; var exitTranslateAnim = new DoubleAnimation { From = 0, To = -20, Duration = TimeSpan.FromMilliseconds(300) }; Storyboard.SetTarget(exitOpacityAnim, oldFrame); Storyboard.SetTargetProperty(exitOpacityAnim, "Opacity"); Storyboard.SetTarget(exitTranslateAnim, oldFrame); Storyboard.SetTargetProperty(exitTranslateAnim, "RenderTransform.(TranslateTransform.X)"); exitStoryboard.Children.Add(exitOpacityAnim); exitStoryboard.Children.Add(exitTranslateAnim); // 创建进入动画:新Frame淡入+向右平移 var enterStoryboard = new Storyboard(); var enterOpacityAnim = new DoubleAnimation { From = 0, To = 1, Duration = TimeSpan.FromMilliseconds(300) }; var enterTranslateAnim = new DoubleAnimation { From = 20, To = 0, Duration = TimeSpan.FromMilliseconds(300) }; Storyboard.SetTarget(enterOpacityAnim, newFrame); Storyboard.SetTargetProperty(enterOpacityAnim, "Opacity"); Storyboard.SetTarget(enterTranslateAnim, newFrame); Storyboard.SetTargetProperty(enterTranslateAnim, "RenderTransform.(TranslateTransform.X)"); enterStoryboard.Children.Add(enterOpacityAnim); enterStoryboard.Children.Add(enterTranslateAnim); // 先播放退出动画,完成后播放进入动画 exitStoryboard.Completed += (s, e) => { oldFrame.Visibility = Visibility.Collapsed; newFrame.Visibility = Visibility.Visible; enterStoryboard.Begin(); }; exitStoryboard.Begin(); }
使用这个方法时,要确保每个Frame都添加了TranslateTransform作为RenderTransform,否则平移动画不会生效。
最后再提个小建议:切换Frame前最好先暂停旧Frame的导航栈动画,避免和切换动画冲突,等切换完成后再恢复,这样整体体验会更流畅。
内容的提问来源于stack exchange,提问作者Caleb Powell

