C# 使用MVVM动态查看动画

C# 使用MVVM动态查看动画,c#,xaml,animation,mvvm,coloranimation,C#,Xaml,Animation,Mvvm,Coloranimation,我一直在试图找出如何在ViewModel中的属性更新时有效地触发视图中的动画,其中动画取决于所述属性的值 我用一个视图和视图模型在一个简单的应用程序中重新创建了我的问题。这里的目标是通过使用彩色动画来转换矩形的颜色变化。作为参考,我一直在使用Josh Smith的软件包 示例项目可以是 总之,我想在“颜色”属性更改时在视图中设置颜色过渡的动画 main window.xaml <Window x:Class="MVVM.ColorAnimation.MainWindow"

我一直在试图找出如何在ViewModel中的属性更新时有效地触发视图中的动画,其中动画取决于所述属性的值

我用一个视图和视图模型在一个简单的应用程序中重新创建了我的问题。这里的目标是通过使用彩色动画来转换矩形的颜色变化。作为参考,我一直在使用Josh Smith的软件包

示例项目可以是

总之,我想在“颜色”属性更改时在视图中设置颜色过渡的动画

main window.xaml

<Window x:Class="MVVM.ColorAnimation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ColorAnimation="clr-namespace:MVVM.ColorAnimation" Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <ColorAnimation:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>

        <Rectangle Margin="10">
            <Rectangle.Fill>
                <SolidColorBrush Color="{Binding Color}"/>
            </Rectangle.Fill>
        </Rectangle>

        <StackPanel Orientation="Horizontal" Grid.Row="1">
            <Button Command="{Binding BlueCommand}" Width="100">Blue</Button>
            <Button Command="{Binding GreenCommand}" Width="100">Green</Button>
        </StackPanel>
    </Grid>
</Window>
是否从视图中触发彩色动画,而不是值之间的即时转换?我目前执行此操作的方式是另一个应用程序非常混乱,因为我通过
ViewModel
属性设置ViewModel,然后使用PropertyObserver监视值更改,然后创建动画并从codebehind触发它:

this.colorObserver = new PropertyObserver<Player>(value)
    .RegisterHandler(n => n.Color, this.CreateColorAnimation);
this.colorObserver=新属性Observer(值)
.RegisterHandler(n=>n.Color,this.CreateColorAnimation);

在我处理许多颜色和许多潜在动画的情况下,这会变得非常混乱,并且会弄乱我手动将ViewModel传递到视图的事实,而不是简单地通过ResourceDictionary绑定两者。我想我也可以在
DataContextChanged
事件中这样做,但是有更好的方法吗?

如果只是一些动画,我建议使用视觉状态。然后可以在视图上使用GoToAction行为来触发不同的动画。如果您正在处理大量类似的动画,那么创建自己的行为将是一个更好的解决方案

更新 我创建了一个非常简单的Behavior,为矩形添加了一点彩色动画。这是代码

   public class ColorAnimationBehavior : TriggerAction<FrameworkElement>
    {
        #region Fill color
        [Description("The background color of the rectangle")]
        public Color FillColor
        {
            get { return (Color)GetValue(FillColorProperty); }
            set { SetValue(FillColorProperty, value); }
        }

        public static readonly DependencyProperty FillColorProperty =
            DependencyProperty.Register("FillColor", typeof(Color), typeof(ColorAnimationBehavior), null);
        #endregion

        protected override void Invoke(object parameter)
        {
            var rect = (Rectangle)AssociatedObject;

            var sb = new Storyboard();
            sb.Children.Add(CreateVisibilityAnimation(rect, new Duration(new TimeSpan(0, 0, 1)), FillColor));

            sb.Begin();
        }

        private static ColorAnimationUsingKeyFrames CreateVisibilityAnimation(DependencyObject element, Duration duration, Color color)
        {
            var animation = new ColorAnimationUsingKeyFrames();

            animation.KeyFrames.Add(new SplineColorKeyFrame { KeyTime = new TimeSpan(duration.TimeSpan.Ticks), Value = color });

            Storyboard.SetTargetProperty(animation, new PropertyPath("(Shape.Fill).(SolidColorBrush.Color)"));
            Storyboard.SetTarget(animation, element);

            return animation;
        }

    }
公共类ColorAnimationBehavior:TriggerAction { #区域填充颜色 [说明(“矩形的背景色”)] 公共颜色填充颜色 { 获取{return(Color)GetValue(FillColorProperty);} set{SetValue(FillColorProperty,value);} } 公共静态只读DependencyProperty FillColorProperty= DependencyProperty.Register(“FillColor”、typeof(Color)、typeof(ColorAnimationBehavior)、null); #端区 受保护的覆盖无效调用(对象参数) { var rect=(矩形)关联对象; var sb=新故事板(); sb.Children.Add(CreateVisibilityAnimation(rect,new Duration(new TimeSpan(0,0,1)),FillColor)); 某人开始做某事; } 私有静态彩色动画使用关键帧CreateVisibilityAnimation(DependencyObject元素、持续时间、颜色) { var animation=使用关键帧()的新彩色动画; 添加(新的SplineColorKeyFrame{KeyTime=newTimeSpan(duration.TimeSpan.Ticks),Value=color}); Storyboard.SetTargetProperty(动画,新属性路径(((Shape.Fill)。(SolidColorBrush.Color)”); 故事板.SetTarget(动画、元素); 返回动画; } } 在xaml中,您只需像这样附加此行为

    <Rectangle x:Name="rectangle" Fill="Black" Margin="203,103,217,227" Stroke="Black">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseLeftButtonDown">
                <local:ColorAnimationBehavior FillColor="Red"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Rectangle>


单击矩形时,它应该从黑色变为红色。

我使用了Xin发布的代码,并做了一些非常小的改动(代码如下)。唯一的三个重要区别是:

我创建的行为适用于任何UIElement,而不仅仅是矩形

我使用了PropertyChangedTrigger而不是EventTrigger。让我们监视ViewModel上的color属性,而不是侦听单击事件

我将FillColor绑定到ViewModel的Color属性

要使用它,您需要下载Blend 4 SDK(它是免费的,只有在您还没有Expression Blend时才需要它),并添加对System.Windows.Interactivity和Microsoft.Expression.Interactivity的引用

以下是行为类的代码:


// complete code for the animation behavior
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace ColorAnimationBehavior
{
    public class ColorAnimationBehavior: TriggerAction<UIElement>
    {
        public Color FillColor
        {
            get { return (Color)GetValue(FillColorProperty); }
            set { SetValue(FillColorProperty, value); }
        }

        public static readonly DependencyProperty FillColorProperty =
            DependencyProperty.Register("FillColor", typeof(Color), typeof(ColorAnimationBehavior), null);

        public Duration Duration
        {
            get { return (Duration)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Duration.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.Register("Duration", typeof(Duration), typeof(ColorAnimationBehavior), null);

        protected override void Invoke(object parameter)
        {
            var storyboard = new Storyboard();
            storyboard.Children.Add(CreateColorAnimation(this.AssociatedObject, this.Duration, this.FillColor));
            storyboard.Begin();
        }

        private static ColorAnimationUsingKeyFrames CreateColorAnimation(UIElement element, Duration duration, Color color)
        {
            var animation = new ColorAnimationUsingKeyFrames();
            animation.KeyFrames.Add(new SplineColorKeyFrame() { KeyTime = duration.TimeSpan, Value = color });
            Storyboard.SetTargetProperty(animation, new PropertyPath("(Shape.Fill).(SolidColorBrush.Color)"));
            Storyboard.SetTarget(animation, element);
            return animation;
        }
    }
}


//动画行为的完整代码
使用System.Windows;
使用System.Windows.Interactive;
使用System.Windows.Media;
使用System.Windows.Media.Animation;
命名空间ColorAnimationBehavior
{
公共类ColorAnimationBehavior:TriggerAction
{
公共颜色填充颜色
{
获取{return(Color)GetValue(FillColorProperty);}
set{SetValue(FillColorProperty,value);}
}
公共静态只读DependencyProperty FillColorProperty=
DependencyProperty.Register(“FillColor”、typeof(Color)、typeof(ColorAnimationBehavior)、null);
公众持续时间
{
获取{return(Duration)GetValue(DurationProperty);}
set{SetValue(DurationProperty,value);}
}
//使用DependencyProperty作为持续时间的后备存储。这将启用动画、样式、绑定等。。。
公共静态只读DependencyProperty DurationProperty=
DependencyProperty.Register(“Duration”,typeof(Duration),typeof(ColorAnimationBehavior),null);
受保护的覆盖无效调用(对象参数)
{
var storyboard=新故事板();
storyboard.Children.Add(CreateColorAnimation(this.AssociatedObject,this.Duration,this.FillColor));
故事板。开始();
}
私有静态彩色动画使用关键帧CreateColorAnimation(UIElement元素、持续时间、颜色)
{
var animation=使用关键帧()的新彩色动画;
添加(新的SplineColorKeyFrame(){KeyTime=duration.TimeSpan,Value=color});
Storyboard.SetTargetProperty(动画,新属性路径(((Shape.Fill)。(SolidColorBrush.Color

// complete code for the animation behavior
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace ColorAnimationBehavior
{
    public class ColorAnimationBehavior: TriggerAction<UIElement>
    {
        public Color FillColor
        {
            get { return (Color)GetValue(FillColorProperty); }
            set { SetValue(FillColorProperty, value); }
        }

        public static readonly DependencyProperty FillColorProperty =
            DependencyProperty.Register("FillColor", typeof(Color), typeof(ColorAnimationBehavior), null);

        public Duration Duration
        {
            get { return (Duration)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Duration.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.Register("Duration", typeof(Duration), typeof(ColorAnimationBehavior), null);

        protected override void Invoke(object parameter)
        {
            var storyboard = new Storyboard();
            storyboard.Children.Add(CreateColorAnimation(this.AssociatedObject, this.Duration, this.FillColor));
            storyboard.Begin();
        }

        private static ColorAnimationUsingKeyFrames CreateColorAnimation(UIElement element, Duration duration, Color color)
        {
            var animation = new ColorAnimationUsingKeyFrames();
            animation.KeyFrames.Add(new SplineColorKeyFrame() { KeyTime = duration.TimeSpan, Value = color });
            Storyboard.SetTargetProperty(animation, new PropertyPath("(Shape.Fill).(SolidColorBrush.Color)"));
            Storyboard.SetTarget(animation, element);
            return animation;
        }
    }
}


<UserControl x:Class="MVVM.ColorAnimation.Silverlight.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:ColorAnimation="clr-namespace:MVVM.ColorAnimation"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
    xmlns:ca="clr-namespace:ColorAnimationBehavior;assembly=ColorAnimationBehavior"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <UserControl.DataContext>
        <ColorAnimation:MainWindowViewModel />
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>

        <Rectangle x:Name="rectangle" Margin="10" Stroke="Black" Fill="Red">
            <i:Interaction.Triggers>
                <ei:PropertyChangedTrigger Binding="{Binding Color}">
                    <ca:ColorAnimationBehavior FillColor="{Binding Color}" Duration="0:0:0.5" />
                </ei:PropertyChangedTrigger>
            </i:Interaction.Triggers>
        </Rectangle>
        <StackPanel Orientation="Horizontal" Grid.Row="1">
            <Button Command="{Binding BlueCommand}" Width="100" Content="Blue"/>
            <Button Command="{Binding GreenCommand}" Width="100" Content="Green"/>
        </StackPanel>
    </Grid>
</UserControl>