Using data binding to launch animations in WPF

2019-04-06 23:41发布

问题:

I am trying to adapt a simple WPF application to use the Model-View-ViewModel pattern. On my page I have a couple of animations:

<Page.Resources>
    <Storyboard x:Name="storyboardRight"
                x:Key="storyboardRight">
        <DoubleAnimation x:Name="da3"
                         Storyboard.TargetName="labelRight"
                         Storyboard.TargetProperty="Opacity"
                         From="0"
                         To="1"
                         Duration="0:0:0.5" />
        <DoubleAnimation x:Name="da4"
                         Storyboard.TargetName="labelRight"
                         Storyboard.TargetProperty="Opacity"
                         From="1"
                         To="0"
                         BeginTime="0:0:1"
                         Duration="0:0:0.5" />
    </Storyboard>
    ...
</Page.Resources>

Currently I begin the animation in the code behind, and can listen to the Completed event to do something when it finishes with the following code:

storyboardRight = (Storyboard)TryFindResource("storyboardRight");
storyboardRight.Completed += new EventHandler(storyboardRight_Completed);
storyboardRight.Begin(this);

Is there a way of data binding the storyboard to my ViewModel so that it starts on an event raised by the ViewModel and can call-back into that ViewModel when it is finished?

回答1:

I had the opportunity to put this question to Microsoft's Josh Twist, who kindly took the time to provide an answer to this problem. The solution is to use a DataTrigger in combination with an enum in the ViewModel to launch the Storyboard, and this in turn requires putting the page into a ContentPresenter. To handle animation completion, a small amount of code behind was required to make a call into an ICommand on the ViewModel.

Read Josh's post here for a full description of the solution.



回答2:

I did this by a using DataTrigger and binding it to a property in my ViewModel. When the "FlashingBackGround" property gets set to "ON" the Storyboard animation starts.

Also make sure to include in your project a reference to "Microsoft.Expression.Interactions"

XAML: (this goes directly in the root node)

<Window
   xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
   xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
   x:Name="window" >
    ...

    <i:Interaction.Triggers>
      <ei:DataTrigger Binding="{Binding FlashingBackground, Mode=OneWay}" Value="ON">
        <ei:ControlStoryboardAction Storyboard="{StaticResource MyAnimation}"     
                                                ControlStoryboardOption="Play"/>
      </ei:DataTrigger>
    </i:Interaction.Triggers>

    ...
</Window>

ViewModel:

    private void TurnOnFlashingBackround()
    {
        this.FlashingBackground = "ON";
    }

    private string _FlashingBackround = "OFF";

    public string FlashingBackground
    {
        get { return this._FlashingBackround; }

        private set
        {
            if (this.FlashingBackground == value)
            {
                return;
            }

            this._FlashingBackround = value;
            this.OnPropertyChanged("FlashingBackground");
        }
    }

    public new event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(
                this, 
                new PropertyChangedEventArgs(propertyName));
        }
    }

Finally, the Viewmodel must inherit from "INotifyPropertyChanged"



回答3:

You need to use an EventTrigger. This article about Animations in WPF might help. See also the Routed Events Overview on the MSDN and How to: Use Event Triggers to Control a Storyboard After It Starts.