wpf - 使用 VisualStateManager 动画 MVVM ViewModel 转换——动画未运行

标签 wpf animation mvvm mvvm-light visualstatemanager

我正在开发我的第一个 WPF 项目,基于 MVVM Light 工具包。主视图在数据绑定(bind)中保存任意 ViewModel ContentControl ,像这样:

<ContentControl x:Name="ViewModelContent" Content="{Binding CurrentViewModel}" ... />

我想做的是,每当这个 ViewModel 发生变化时,淡出旧的 ViewModel(即 ContentControl ),然后淡入新的。这在 WPF 世界中并不是一个新想法,我花了相当多的时间研究如何实现它(在这里和其他网站上)。我试图保持这个相对简单,我收集到的是我可以使用 VisualStateManager在 View 的 DataTemplate 上定义两个状态:
  • ContentChanging:淡出状态
  • ContentChanged:淡入状态

  • 然后,使用 GoToStateAction ,我可以根据需要动画到适当的状态。就我而言,“根据需要”由 CurrentViewModelChanging 完成。在我的 ViewModel 上定义的属性(请记住,我使用的是 MVVM Light):
    private bool _vmChanging;
    public bool CurrentViewModelChanging
    {
        get
        {
            return _vmChanging;
        }
        private set
        {
            Set(() => CurrentViewModelChanging, ref _vmChanging, value);
        }
    }
    

    然后我可以使用 DataTrigger绑定(bind)到此属性并相应地更改状态。在 DataTemplate 的 XAML 中它看起来像这样:
    <i:Interaction.Triggers>
        <ei:DataTrigger Binding="{Binding CurrentViewModelChanging, Mode=OneWay}" Value="True">
            <ei:GoToStateAction StateName="ContentChanging"/>
        </ei:DataTrigger>
        <ei:DataTrigger Binding="{Binding CurrentViewModelChanging, Mode=OneWay}" Value="False">
            <ei:GoToStateAction StateName="ContentChanged"/>
        </ei:DataTrigger>
    </i:Interaction.Triggers>
    

    到现在为止还挺好。我在表单上放了一个按钮,除了切换 CurrentViewModelChanging 之外没有其他用途。 (从而改变状态),它完全符合我的预期:单击它一次会淡出 View ,然后再次单击它会淡入它。当我在实际更改 View 时尝试完成淡出/淡入时会出现问题 View 模型。这是我尝试这个的代码片段:
    public Standards.StandardsViewModel CurrentViewModel
    {
        ....
        private set
        {
            ....
            CurrentViewModelChanging = true;
            Set(() => CurrentViewModel, ref _viewModel, value);
            CurrentViewModelChanging = false;
            ....
        };
    }
    

    当它运行时会发生什么基本上什么都没有:ViewModel 在没有任何动画的情况下立即切换。我的印象是更改 CurrentViewModelChanging将导致动画运行并停止执行任何剩余的代码,直到动画完成。情况似乎并非如此,所以有人可以告诉我正在发生的事情(以及如何解决它)吗?我最好的猜测是动画在与执行逻辑不同的线程中运行,并且所述逻辑执行得如此之快,以至于动画没有时间实际做任何事情。但是,如果我把 Thread.Sleep()调用CurrentViewModelChanging切换然后我仍然没有得到动画:无论我告诉它多久,程序都会挂起,然后立即更改 ViewModel。

    此外,如果不明显,这一切都基于 DataTemplate ,所以解决方案需要我使用 UserControls不理想。但是,如果这就是它所需要的,那么我肯定会这样做。这是我的 DataTemplate 的更完整的 XAML ,如果有帮助的话。请注意,为了保持简洁,我省略了动画的细节:
    <DataTemplate DataType="{x:Type localVMS:StandardsModuleViewModel}">
        <DockPanel x:Name="ModLayout" LastChildFill="True" Margin="0" Grid.Column="1">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="ContentPresentationStates">
                    <VisualStateGroup.Transitions>
                        <VisualTransition GeneratedDuration="0:0:0.1" To="ContentChanging"/>
                        <VisualTransition GeneratedDuration="0:0:0.1" To="ContentChanged"/>
                    </VisualStateGroup.Transitions>
                    <VisualState x:Name="ContentChanging" ... />
                    <VisualState x:Name="ContentChanged" .... />
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            <Grid x:Name="MainMenuGrid" VerticalAlignment="Top" Background="Black" DockPanel.Dock="Top">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <ItemsControl x:Name="MainMenu" HorizontalAlignment="Left" VerticalAlignment="Bottom" ItemsSource="{Binding MainMenu}" ItemTemplate="{DynamicResource TopMenuItem}" ItemsPanel="{DynamicResource HorizontalMenuTemplate}" FontFamily="Calibri" FontSize="16" MinHeight="20" Background="Transparent" VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
            </Grid>
            <ItemsControl x:Name="SubMenu" ItemsSource="{Binding SubMenu}" ItemsPanel="{DynamicResource HorizontalMenuTemplate}" ItemTemplate="{StaticResource SubMenuItem}" DockPanel.Dock="Top" Height="25" VerticalContentAlignment="Center" Background="White" FontFamily="Calibri" FontSize="13.333"/>
            <Grid x:Name="ViewModelLayout" DockPanel.Dock="Bottom">
                <i:Interaction.Triggers>
                    <ei:DataTrigger Binding="{Binding CurrentViewModelChanging, Mode=OneWay}" Value="True">
                        <ei:GoToStateAction StateName="ContentChanging"/>
                    </ei:DataTrigger>
                    <ei:DataTrigger Binding="{Binding CurrentViewModelChanging, Mode=OneWay}" Value="False">
                        <ei:GoToStateAction StateName="ContentChanged"/>
                    </ei:DataTrigger>
                </i:Interaction.Triggers>
                <ContentControl x:Name="ViewModelContent" Content="{Binding CurrentViewModel}" ContentTemplateSelector="{StaticResource GenericTemplateSelector}" HorizontalAlignment="Center" VerticalAlignment="Top" Padding="10" HorizontalContentAlignment="Center" Margin="10" RenderTransformOrigin="0.5,0.5" ... />
            </Grid>
        </DockPanel>
    </DataTemplate>
    

    最佳答案

    问题是您的 ContentControl 一次只能绑定(bind)到一件事。一旦您更改 View 模型,与旧 View 模型的绑定(bind)就不再存在,因此没有什么可以褪色的。要解决此问题,您要么必须设置两个重叠的用户控件并分别淡入/淡出它们,要么使用 a helper class为你照顾它。

    关于wpf - 使用 VisualStateManager 动画 MVVM ViewModel 转换——动画未运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26914711/

    相关文章:

    c# - 从不同线程传递数据时,是否有其他方法可以在 WPF 中调用?

    c# - 如何将 dropshadoweffect 添加到文本框的文本(以编程方式)

    即使我使用新线程,WPF 动画也会卡住

    javascript - 如何延迟CSS上的动画?我试过动画延迟。不起作用

    c# - 如何通过 ViewModel 控制 View VisualState

    c# - WPF Datagrid 上的选定行 LostFocus mvvm 模式

    c# - 使用 Teststack.White 将文本写入 PasswordBox

    jquery - 制作 div 动画并顺时针旋转。即 Div 的 radialwipe 时钟效果

    c# - InvalidCastException : Unable to cast object of type 'Mocks.Class_…' to 'Class'

    c# - 发生 "Ensure Event Failed"错误