Anton Kandybo
Anton Kandybo

Reputation: 4068

Update property in ViewModel during DoubleAnimation in WPF

I have WPF solution which uses MVVM pattern. It contains ViewModel BlockViewModel:

public class BlockViewModel:ViewModelBase
{
    private double _top;
    public double Top
    {
        get
        {
            return _top;
        }
        set
        {
            if (_top == value)
            {
                return;
            }
            _top = value;
            OnPropertyChanged("Top");
        }
    }

    private bool _isAnimated;
    public bool IsAnimated
    {
        get { return _isAnimated; }
        set
        {
            if (_isAnimated == value)
            {
                return;
            }
            _isAnimated = value;
            OnPropertyChanged("IsAnimated");
        }
    }

}

And UserControl BlockView:

<UserControl 
             x:Class="BlockGame.Wpf.Views.BlockView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:converters="clr-namespace:BlockGame.Wpf.Converters"
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
             xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
             xmlns:blocks="clr-namespace:BlockGame.ViewModels.Blocks;assembly=BlockGame.ViewModels"

             mc:Ignorable="d"
             d:DataContext="{d:DesignInstance Type=blocks:BlockViewModel, IsDesignTimeCreatable=True}"
             >
    <UserControl.Resources>
        <Storyboard x:Key="BlockStoryboard">
            <DoubleAnimation x:Name="StoryboardAnimation" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.Y)" Storyboard.TargetName="Border" From="0" To="100" Duration="0:0:5" />
        </Storyboard>

    </UserControl.Resources>

    <Border Width="100" Height="50" Name="Border" Background="Lavender" BorderThickness="1" BorderBrush="Black">

        <Border.RenderTransform>
            <TranslateTransform X="20" Y="{Binding Top}" />
        </Border.RenderTransform>

        <i:Interaction.Triggers>
            <ei:DataTrigger Binding="{Binding IsAnimated}" Value="True">
                <ei:ControlStoryboardAction Storyboard="{StaticResource BlockStoryboard}" />
            </ei:DataTrigger>
            <ei:DataTrigger Binding="{Binding IsAnimated}" Value="False">
                <ei:ControlStoryboardAction Storyboard="{StaticResource BlockStoryboard}" ControlStoryboardOption="Pause" />
            </ei:DataTrigger>
        </i:Interaction.Triggers>

    </Border>
</UserControl>

Is there any way to update Top property during animation?

UPDATE

It seems I found working solution:

<Border.RenderTransform>
    <TranslateTransform X="{Binding Left}" Y="0" x:Name="TranslateTransform" />
</Border.RenderTransform>
<i:Interaction.Triggers>
         ...
    <ei:PropertyChangedTrigger Binding="{Binding ElementName=TranslateTransform, Path=Y}">
        <ei:ChangePropertyAction Value="{Binding ElementName=TranslateTransform, Path=Y}" PropertyName="Top" TargetObject="{Binding RelativeSource={RelativeSource  FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext}"></ei:ChangePropertyAction>
    </ei:PropertyChangedTrigger>
</i:Interaction.Triggers>

Upvotes: 3

Views: 1402

Answers (1)

Asim Mittal
Asim Mittal

Reputation: 101

Interesting question. Animations in WPF target dependency properties and when you start them there isn't a good way to intervene from time to time. One way could be by using a custom timeline for your animation. This allows you to define your own timing mechanism for the animation. Again, haven't tried this out myself.

Looking at your code, i see that the Top property is bound to the Y property of the border. So changing the top property could essentially just be changing the Y property of your border's render transform.

Why don't you simply animate the border directly instead of animating something and trying to change Top in the process. If you want to sync your border animation with another animation, you can create two double animation objects with similar To, From and Interval settings. You can start both together to achieve the effect you're looking for.

Here's an example:

ScaleTransform st = new ScaleTransform();
this.RenderTransformOrigin = new Point(0.5, 0.5);
this.RenderTransform = st;
st.BeginAnimation(ScaleTransform.ScaleXProperty, scaleUp);
st.BeginAnimation(ScaleTransform.ScaleYProperty, scaleUp);

Here I'm applying the same animation to two different properties of the same transform to scale something proportionately. You could do the same thing two both the object and its border element

Upvotes: 2

Related Questions