【问题标题】:MVVM Navigation Switching ViewModels causes an error with animationsMVVM 导航切换视图模型导致动画错误
【发布时间】:2020-02-07 15:38:42
【问题描述】:

我正在尝试使用 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}" />

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

我通过 从实现中介者模式的 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>

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

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

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

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

【问题讨论】:

    标签: c# wpf mvvm navigation


    【解决方案1】:

    我找到了一个有效的解决方案,但我不确定为什么它有效。 我删除了所有与动画相关的 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 版本不起作用,所以我仍然想知道这个问题的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-08
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 2017-03-20
      • 1970-01-01
      相关资源
      最近更新 更多