Animating MVVM ViewModel transitions using VisualStateManager - animation not working

I am working on my first WPF project based on MVVM Light toolkit. The main view contains an arbitrary ViewModel in a data binding ContentControl

, for example:

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

      

What I would like to do is, whenever this ViewModel changes, the old ViewModel (that is, ContentControl

) disappears and then disappears into the new one. This is not a new idea in the WPF world and I've spent quite a bit of time learning how to accomplish it (both here and elsewhere). I'm trying to keep this relatively simple, and I've put together what I can use VisualStateManager

to define two states in a view DataTemplate

:

  • ContentChanging: the processed state
  • ContentChanged: faded-in state

Then using GoToStateAction

, I can approach the appropriate state as needed. In my case, the property CurrentViewModelChanging

defined on my ViewModel is executed "as needed" (remember, I am using MVVM Light):

private bool _vmChanging;
public bool CurrentViewModelChanging
{
    get
    {
        return _vmChanging;
    }
    private set
    {
        Set(() => CurrentViewModelChanging, ref _vmChanging, value);
    }
}

      

Then I can use DataTrigger

to bind to that property and change the state accordingly. In XAML for, DataTemplate

it looks like this:

<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>

      

So far so good. I put a button in my form, which serves no other purpose than toggle CurrentViewModelChanging

(and thus change state), and it does exactly what I expected: a click on it once disappears from view, and a click fades back out again The problem occurs when I try to fade out when the ViewModel actually changes. Here is a code snippet where I try it:

public Standards.StandardsViewModel CurrentViewModel
{
    ....
    private set
    {
        ....
        CurrentViewModelChanging = true;
        Set(() => CurrentViewModel, ref _viewModel, value);
        CurrentViewModelChanging = false;
        ....
    };
}

      

What happens when this is executed is essentially nothing: the ViewModel instantly switches without animation. I was under the impression that the change CurrentViewModelChanging

would trigger the animation and stop executing the remaining code before the animation finishes. This doesn't seem to be the case, so can someone please tell me what is going on (and how to fix it)? My best guess is that the animation runs on a different thread than the execution logic, and that logic runs so fast that there is no time for the animations to actually do nothing. However, if I put the call Thread.Sleep()

inside the switch CurrentViewModelChanging

, then I still don't get the animation: the program just freezes, no matter how long I say, and then immediately changes the ViewModel.

Also, if it wasn't obvious, this is all based on DataTemplate

, so the solutions requiring me to use UserControls

are not ideal. However, if this happens, I will definitely do it. Here's a more complete XAML for mine DataTemplate

, if it helps. Note that I left out the animation details to keep things concise:

<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>

      

+3


source to share


1 answer


The problem is that your ContentControl can only bind to one thing at a time. Once you change your view model, the binding to the old one no longer exists, so nothing will disappear. To fix this, you'll either have to set up the two overlapping UserControls, expose them separately, or use a helper class to take care of this for you.



0


source







All Articles