WPF动画基于属性值

时间:2013-04-18 13:27:24

标签: wpf animation binding

我有一个依赖属性双文,我想要完成的是每当我改变<的值时,用文本度旋转(用动画)一行strong>文字属性。

有没有比实现OnTextPropertyChanged更好的方法并处理动画?或者可以在XAML中完成?

这是一个演示代码:

MainWindow.xaml

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
        Title="MainWindow" Height="200" Width="200" x:Name="Root">
    <Grid>
        <TextBox Width="50" Height="20" VerticalAlignment="Top"
                 Text="{Binding Text, ElementName=Root, Mode=TwoWay}"/>
        <Grid Width="100" Height="100" Background="Gray" x:Name="Grid">
            <Line Grid.Row="1" X2="100" Y2="100" Stroke="Red"/>
        </Grid>
    </Grid>
</Window>

和MainWindow.xaml.cs(不使用):

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(double), typeof(MainWindow), new FrameworkPropertyMetadata(0.0));

        public double Text
        {
            get { return (double)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

1 个答案:

答案 0 :(得分:1)

这可以通过Binding来实现:

    <TextBox Width="50" Height="20" VerticalAlignment="Top"
             Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <Grid Width="100" Height="100" Background="Gray" x:Name="Grid">
        <Line Grid.Row="1" X2="100" Y2="100" Stroke="Red">
            <Line.RenderTransform>
                <RotateTransform CenterX="50" CenterY="50" Angle="{Binding Path=Text, Mode=OneWay, Converter={StaticResource ResourceKey=SToD}}"/>
            </Line.RenderTransform>
        </Line>
    </Grid>

您的转换器只需将您的字符串转换为双return System.Convert(value.ToString());

我在测试时发现的一个很酷的功能是,如果你没有在TextBox中输入double值,会自动添加验证规则,返回ValidationError。

要完成这项工作,您必须使用this.DataContext = this;将MainWindows DataContext连接到自身。

编辑: 我写了一个小助手类,它为你做动画:

public class DoubleAnimationManager
{
    private static Dictionary<DependencyObject, AnimationHelper> _helperDic = new Dictionary<DependencyObject, AnimationHelper>();

    public static PropertyPath GetProperty(DependencyObject obj)
    {
        return (PropertyPath)obj.GetValue(PropertyProperty);
    }

    public static void SetProperty(DependencyObject obj, PropertyPath value)
    {
        obj.SetValue(PropertyProperty, value);
    }

    public static readonly DependencyProperty PropertyProperty = DependencyProperty.RegisterAttached(
        "Property",
        typeof(PropertyPath),
        typeof(DoubleAnimationManager),
        new PropertyMetadata(
            (o, e) =>
            {
                if (!_helperDic.ContainsKey(o))
                    _helperDic[o] = new AnimationHelper(o);

                _helperDic[o].Path = (PropertyPath)e.NewValue;
            }));

    public static int GetDelayTimeInMS(DependencyObject obj)
    {
        return (int)obj.GetValue(DelayTimeInMSProperty);
    }

    public static void SetDelayTimeInMS(DependencyObject obj, int value)
    {
        obj.SetValue(DelayTimeInMSProperty, value);
    }

    public static readonly DependencyProperty DelayTimeInMSProperty = DependencyProperty.RegisterAttached(
        "DelayTimeInMS",
        typeof(int),
        typeof(DoubleAnimationManager),
        new PropertyMetadata(0,
            (o, e) =>
            {
                if (!_helperDic.ContainsKey(o))
                    _helperDic[o] = new AnimationHelper(o);

                _helperDic[o].DelayInMS = (int)e.NewValue;
            }));

    public static double GetValue(DependencyObject obj)
    {
        return (double)obj.GetValue(ValueProperty);
    }

    public static void SetValue(DependencyObject obj, double value)
    {
        obj.SetValue(ValueProperty, value);
    }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
        "Value",
        typeof(double),
        typeof(DoubleAnimationManager),
        new PropertyMetadata(
            (o, e) =>
            {
                if (!_helperDic.ContainsKey(o))
                    _helperDic[o] = new AnimationHelper(o);

                _helperDic[o].ToValue = (double)e.NewValue;
                if (!_helperDic[o].Started)
                {
                    _helperDic[o].FromValue = (double)e.OldValue;
                    _helperDic[o].Start();
                }
            }));
}

public class AnimationHelper
{
    private bool _remMode;
    private DependencyObject _target;
    private BackgroundWorker _bw = new BackgroundWorker();
    public double FromValue { get; set; }
    private double _toValue;
    public double ToValue 
    {
        get { return _toValue; }
        set
        {
            if (Started)
            {
                _remMode = true;
            }
            _toValue = value;
        }
    }
    public bool Started { get; private set; }
    private int _delayInMS;
    public int DelayInMS 
    {
        get { return _delayInMS; }
        set
        {
            _delayInMS = value;
        }
    }
    public PropertyPath Path { get; set; }

    public AnimationHelper(DependencyObject target)
    {
        _target = target;
        _bw.DoWork += delegate
        {
            System.Threading.Thread.Sleep(DelayInMS);
        };
        _bw.RunWorkerCompleted += delegate
        {
            StartAnimation();
        };
    }

    private Storyboard GetStoryboard()
    {
        Storyboard sb = new Storyboard();
        DoubleAnimation da = new DoubleAnimation(ToValue, new TimeSpan(0, 0, 3));
        da.From = FromValue;
        Storyboard.SetTarget(da, _target);
        Storyboard.SetTargetProperty(da, Path);
        sb.Children.Add(da);
        sb.Completed += delegate
        {
            if (_remMode)
            {
                StartAnimation();
                _remMode = false;
            }
            else
            {
                Started = false;
            }
        };
        return sb;
    }

    private void StartAnimation()
    {
        GetStoryboard().Begin();
        FromValue = ToValue;
    }

    public void Start()
    {
        Started = true;
        _bw.RunWorkerAsync();
    }
}

如何使用它:

    <TextBox Width="50" Height="20" VerticalAlignment="Top"
             Text="{Binding Path=Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <Grid Width="100" Height="100" Background="Gray" x:Name="Grid">
        <Line Grid.Row="1" X2="100" Y2="100" Stroke="Red" local:DoubleAnimationManager.Property="(UIElement.RenderTransform).(RotateTransform.Angle)" local:DoubleAnimationManager.Value="{Binding Path=Text, Mode=OneWay, Converter={StaticResource ResourceKey=SToD}}">
            <Line.RenderTransform>
                <RotateTransform CenterX="50" CenterY="50"/>
            </Line.RenderTransform>
        </Line>
    </Grid>