MVVM 导航切换视图模型会导致动画错误



我正在尝试使用 MVVM 模式制作 WPF 应用程序。 我现在希望能够在 ViewModel 之间导航,为此我使用了这篇文章:https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/

基本上,主窗口资源包含每个视图模型的 DataTemplates,将其链接到其视图,以及绑定到 CurrentPageViewModel 的内容控件,如下所示:

<Window.Resources>
<DataTemplate DataType="{x:Type ViewModels:HomeViewModel}">
<Views:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:VaultViewModel}">
<Views:VaultView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:VaultsViewModel}">
<Views:VaultsView />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentPageViewModel}" />

在主窗口的构造函数中,我将当前页面视图模型设置为我的主屏幕视图模型的新实例,当我想导航时,我需要做的就是更改当前页面视图模型,WPF 将完成剩下的工作,因为我使用了INotifyPropertyChanged

我通过从实现中介模式的MVVM Light 信使复制代码,从我的视图模型向主窗口发送更改页面请求,这工作正常,除非我播放故事板。

我的主页内有两个非常漂亮的按钮,当您将鼠标悬停在它们上时,它们会增长一点:

<Button Style="{DynamicResource MaterialButton}" Width="400px" Height="400px" Margin="15px" 
FontSize="24" FontFamily="Roboto" FontWeight="Bold" 
Effect="{DynamicResource HomeButtonDropShadowEffect}" Command="{Binding DisplayVaultsView}" 
Content="Vaults">
<!--#region button effects -->
<Button.RenderTransform>
<ScaleTransform x:Name="scaleTransform" CenterX="200" CenterY="200" ScaleX="1.0" ScaleY="1.0"/>
</Button.RenderTransform>
<Button.Triggers>
<EventTrigger RoutedEvent="Button.MouseEnter">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation 
Storyboard.TargetName="scaleTransform"
Storyboard.TargetProperty="ScaleX"
To="1.08" Duration="0:0:0.1"/>
<DoubleAnimation 
Storyboard.TargetName="scaleTransform"
Storyboard.TargetProperty="ScaleY"
To="1.08" Duration="0:0:0.1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Button.MouseLeave">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation 
Storyboard.TargetName="scaleTransform"
Storyboard.TargetProperty="ScaleX"
To="1.0" Duration="0:0:0.1" FillBehavior="Stop"/>
<DoubleAnimation 
Storyboard.TargetName="scaleTransform"
Storyboard.TargetProperty="ScaleY"
To="1.0" Duration="0:0:0.1" FillBehavior="Stop"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
<!--#endregion-->    
</Button>

如您所见,按钮的命令设置为DisplayVaultsView,这是HomeViewModel内部的一种方法,它将更改页面请求发送到主窗口。 按钮内没有这些情节提要,这就可以正常工作,并显示新页面。但是,对于情节提要,单击按钮后会抛出以下错误:System.InvalidOperationException: ''scaleTransform' name cannot be found in the name scope of 'System.Windows.Controls.Button'.'

这让我相信动画仍在尝试播放,但由于按钮不再处于当前上下文中,因此它找不到它应该动画的属性。

同样重要的是要注意,当您导航到另一个页面时,HomeViewModel 不会被丢弃,我已经编写了自己的 Navigator 类来跟踪 ViewModels,因此您可以向前/向后导航而不会丢失任何更改(也不必重新加载所有内容(。

我想知道如何解决此错误,但它也使我意识到在切换到另一个时可能需要"卸载"ViewModels。我目前只是将所有 ViewModel 引用保存在 Navigator 类内的列表中。所以我的另一个问题是,在切换到另一个视图模型时,我应该"卸载"视图模型吗,以及如何卸载?

我找到了一个有效的解决方案,但我不确定它为什么有效。 我删除了与动画相关的所有 XAML,并将动画移动到代码隐藏中HomeView.xaml.

代码如下:

public partial class HomeView : UserControl
{
private Storyboard _mouseEnterStoryboard;
private Storyboard _mouseLeaveStoryboard;
private DoubleAnimation _scaleX_In;
private DoubleAnimation _scaleY_In;
private DoubleAnimation _scaleX_Out;
private DoubleAnimation _scaleY_Out;
public HomeView()
{
InitializeComponent();
_mouseEnterStoryboard = new Storyboard();
ScaleTransform scaleVaultsButton = new ScaleTransform(1.0, 1.0);
ScaleTransform scaleFilesButton = new ScaleTransform(1.0, 1.0);
VaultsButton.RenderTransformOrigin = new Point(0.5, 0.5);
VaultsButton.RenderTransform = scaleVaultsButton;
FilesButton.RenderTransformOrigin = new Point(0.5, 0.5);
FilesButton.RenderTransform = scaleFilesButton;
_scaleX_In = new DoubleAnimation()
{
Duration = TimeSpan.FromMilliseconds(100),
From = 1.0,
To = 1.08
};
_scaleY_In = new DoubleAnimation()
{
Duration = TimeSpan.FromMilliseconds(100),
From = 1.0,
To = 1.08
};
_mouseEnterStoryboard.Children.Add(_scaleX_In);
_mouseEnterStoryboard.Children.Add(_scaleY_In);
Storyboard.SetTargetProperty(_scaleX_In, new PropertyPath("RenderTransform.ScaleX"));
Storyboard.SetTargetProperty(_scaleY_In, new PropertyPath("RenderTransform.ScaleY"));
_mouseLeaveStoryboard = new Storyboard();
_scaleX_Out = new DoubleAnimation()
{
Duration = TimeSpan.FromMilliseconds(100),
From = 1.08,
To = 1.0
};
_scaleY_Out = new DoubleAnimation()
{
Duration = TimeSpan.FromMilliseconds(100),
From = 1.08,
To = 1.0
};
_mouseLeaveStoryboard.Children.Add(_scaleX_Out);
_mouseLeaveStoryboard.Children.Add(_scaleY_Out);
Storyboard.SetTargetProperty(_scaleX_Out, new PropertyPath("RenderTransform.ScaleX"));
Storyboard.SetTargetProperty(_scaleY_Out, new PropertyPath("RenderTransform.ScaleY"));
VaultsButton.MouseEnter += new MouseEventHandler(Button_MouseEnter);
VaultsButton.MouseLeave += new MouseEventHandler(Button_MouseLeave);
FilesButton.MouseEnter += new MouseEventHandler(Button_MouseEnter);
FilesButton.MouseLeave += new MouseEventHandler(Button_MouseLeave);
}
public void Button_MouseEnter(object sender, MouseEventArgs e)
{
Storyboard.SetTarget(_scaleX_In, (Button)sender);
Storyboard.SetTarget(_scaleY_In, (Button)sender);
_mouseEnterStoryboard.Begin();
}
public void Button_MouseLeave(object sender, MouseEventArgs e)
{
Storyboard.SetTarget(_scaleX_Out, (Button)sender);
Storyboard.SetTarget(_scaleY_Out, (Button)sender);
_mouseLeaveStoryboard.Begin();
}
}

我不确定为什么这确实有效而 XAML 版本不起作用,所以我仍然想要这个问题的答案。

最新更新