如何在按钮动画图像在WPF每30秒动摇? [英] How to animate an Image in a button to shake every 30 seconds in WPF?

查看:316
本文介绍了如何在按钮动画图像在WPF每30秒动摇?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我,当涉及到处理与样式和动画什么不好。

I not good when it comes to dealing with anything with styles and animations.

我希望能够得到有关使图像这是一个按钮的唯一内容撼动每30秒的时候曾经按钮可见性设置为Visibility.Visible一些帮助。

I was hoping to be able to get some help on making an Image that is the only content of a Button shake every 30 seconds when ever the buttons Visibility is set to Visibility.Visible.

这是得到用户的关注,鼓励他们单击按钮。

It is to get the users attention to encourage them to click the button.

我想preFER要做到这一点作为图像的附加行为,或者如果可能的话甚至UIControl,使它很容易可重复使用的,而不是与风格搞乱,因为我已经使用了风格,从我的控制供应商,并我不想对其进行编辑。

I would prefer to do this as an attached behavior on Image, or if possible even UIControl, to make it easily reusable instead of messing with the style, as I am already using a style from my control vendor, and I don't want to edit it.

这是我使用标记的答案得出的解决方案

这是该附加的行为可以被应用到任何的 System.Windows.Controls.Image

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Interactivity;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SampleShakeBehavior
{
    public class ShakeBehavior : Behavior<Image>
    {
        private const double DefaultRepeatInterval = 10.0;
        private const double DefaultSpeedRatio = 1.0;

        private const string RepeatIntervalName = "RepeatInterval";
        private const string SpeedRatioName = "SpeedRatio";

        public static readonly DependencyProperty RepeatIntervalProperty =
            DependencyProperty.Register(RepeatIntervalName,
                                        typeof(double),
                                        typeof(ShakeBehavior),
                                        new PropertyMetadata(DefaultRepeatInterval));

        public static readonly DependencyProperty SpeedRatioProperty =
            DependencyProperty.Register(SpeedRatioName,
                                        typeof(double),
                                        typeof(ShakeBehavior),
                                        new PropertyMetadata(DefaultSpeedRatio));

        /// <summary>
        /// Gets or sets the time interval in in seconds between each shake.
        /// </summary>
        /// <value>
        /// The time interval in in seconds between each shake.
        /// </value>
        /// <remarks>
        /// If interval is less than total shake time, then it will shake
        /// constantly without pause. If this is your intention, simply set
        /// interval to 0.
        /// </remarks>
        public double RepeatInterval
        {
            get { return (double)GetValue(RepeatIntervalProperty); }
            set { SetValue(RepeatIntervalProperty, value); }
        }

        /// <summary>
        /// Gets or sets the ratio at which time progresses on the Shakes
        /// Timeline, relative to its parent. 
        /// </summary>
        /// <value> 
        /// The ratio at which time progresses on the Shakes Timeline, relative 
        /// to its parent.
        /// </value>
        /// <remarks> 
        /// If Acceleration or Deceleration are specified, this ratio is the
        /// average ratio over the natural length of the Shake's Timeline. This 
        /// property has a default value of 1.0. If set to zero or less it
        /// will be reset back to th default value.
        /// </remarks>
        public double SpeedRatio
        {
            get { return (double)GetValue(SpeedRatioProperty); }
            set { SetValue(SpeedRatioProperty, value); }
        }

        private Style _orignalStyle;
        protected override void OnAttached()
        {
            _orignalStyle = AssociatedObject.Style;
            AssociatedObject.Style = CreateShakeStyle();
        }

        protected override void  OnDetaching()
        {
            AssociatedObject.Style = _orignalStyle;
        }

        private Style CreateShakeStyle()
        {
            Style newStyle = new Style(AssociatedObject.GetType(), AssociatedObject.Style);
            /**
             * The following will replace/override any existing RenderTransform
             * and RenderTransformOrigin properties on the FrameworkElement
             * once the the new Style is applied to it.
             */
            newStyle.Setters.Add(new Setter(UIElement.RenderTransformProperty, new RotateTransform(0)));
            newStyle.Setters.Add(new Setter(UIElement.RenderTransformOriginProperty, new Point(0.5, 0.5)));

            newStyle.Triggers.Add(CreateTrigger());

            return newStyle;
        }

        private DataTrigger CreateTrigger()
        {
            DataTrigger trigger = new DataTrigger
            {
                Binding = new Binding
                {
                    RelativeSource = new RelativeSource
                    {
                        Mode = RelativeSourceMode.FindAncestor,
                        AncestorType = typeof(UIElement)
                    },
                    Path = new PropertyPath(UIElement.IsVisibleProperty)
                },
                Value = true,
            };

            trigger.EnterActions.Add(new BeginStoryboard { Storyboard = CreateStoryboard() });

            return trigger;
        }

        private Storyboard CreateStoryboard()
        {
            double speedRatio = SpeedRatio;

            // Must be greater than zero
            if (speedRatio <= 0.0)
                SpeedRatio = DefaultSpeedRatio;

            Storyboard storyboard = new Storyboard 
            {
                RepeatBehavior = RepeatBehavior.Forever,
                SpeedRatio = speedRatio
            };

            storyboard.Children.Add(CreateAnimationTimeline());

            return storyboard;
        }

        private Timeline CreateAnimationTimeline()
        {
            DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();

            animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(0).(1)", UIElement.RenderTransformProperty, RotateTransform.AngleProperty));

            int keyFrameCount = 8;
            double timeOffsetInSeconds = 0.25;
            double totalAnimationLength = keyFrameCount * timeOffsetInSeconds;
            double repeatInterval = RepeatInterval;

            // Can't be less than zero and pointless to be less than total length
            if (repeatInterval < totalAnimationLength)
                repeatInterval = totalAnimationLength;

            animation.Duration = new Duration(TimeSpan.FromSeconds(repeatInterval));

            int targetValue = 12;
            for (int i = 0; i < keyFrameCount; i++)
                animation.KeyFrames.Add(new LinearDoubleKeyFrame(i % 2 == 0 ? targetValue : -targetValue, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(i * timeOffsetInSeconds))));

            animation.KeyFrames.Add(new LinearDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(totalAnimationLength))));
            return animation;
        }
    }
}

下面是如何在XAML中使用它。

Here is how to use it in Xaml.

<Button>
    <Image Source="myImage.png">
        <i:Interaction.Behaviors>
            <local:ShakeBehavior RepeatInterval="30" SpeedRatio="3.0"/>
        </i:Interaction.Behaviors>
    </Image>
</Button>

有关一个附加行为的明确定义,你可以看一下 System.Windows.Interactivity .Behavior 类的言论。行为可以有选择有附加属性与他们以及使它们非常有用的。

For a clear definition of an Attached Behavior you can look at the System.Windows.Interactivity.Behavior class remarks. Behaviors can optionally have Attached Properties with them as well make them very useful.

有关附加属性的明确定义,你可以阅读附加属性概述< / A>从MSDN。附加属性可以做任何事情,他们可以被认为是附加的行为,因为他们可以触发一个动作发生引起有效的行为,但在技术上他们仍然只是一个附加属性。

For a clear definition of an Attached Property you can read the Attached Properties Overview from MSDN. Attached properties can do anything, and they can be thought of as attached behaviors because they can trigger an action to occur causing an effective behavior, however technically they are still just an attached property.

由于附加属性可以像一个行为人都来也呼吁这些类型的附加属性附加的行为的时候,其实它是不是真的一个附加行为,除非你的行为,并在它的附加属性得到<一个href=\"http://msdn.microsoft.com/en-us/library/microsoft.internal.pivot.interactivity.interaction.behaviors.aspx\"相对=nofollow> Interaction.Behaviors 集合。

Since an Attached Property can act like a behavior people have come to also call those types of Attached Properties an Attached Behavior, when in fact it is not really an Attached Behavior unless you derive from Behavior and at it to the attached property Interaction.Behaviors collection.

不需要混合任何附加的行为或附加属性,与在WPF / Silverlight中的大多数事情。

Blend is not required for any Attached Behavior or Attached Property, as with most things in WPF/Silverlight.

推荐答案

这是一个附加的行为。只是要小心,因为如果你的控制已使现有的转换可能是毁灭性的。

Here it is an attached behaviour. Just be careful as it may be destructive if your control has existing render transforms

public class Wibble
{
    public static bool GetWobble(DependencyObject obj)
    {
        return (bool)obj.GetValue(WobbleProperty);
    }

    public static void SetWobble(DependencyObject obj, bool value)
    {
        obj.SetValue(WobbleProperty, value);
    }

    public static readonly DependencyProperty WobbleProperty = DependencyProperty.RegisterAttached("Wobble", typeof(bool), typeof(Wibble), new UIPropertyMetadata(false, new PropertyChangedCallback(OnWobbleChanged)));

    private static void OnWobbleChanged(object sender, DependencyPropertyChangedEventArgs args)
    {
        var image = sender as Image;

        if (image == null)
            throw new InvalidOperationException("only images can wobble!");

        // don't really need this check (the find ancestor binding would still find the button), but the spec said the image should be the only child of the button
        var button = LogicalTreeHelper.GetParent(image) as Button;
        if (button == null)
            throw new InvalidOperationException("only images that are the only child of a button can wobble!");

        var previousStyle = image.Style;

        var newStyle = new Style(image.GetType(), previousStyle);

        // this will override any existing render transform + origin on the button, hope they didn't already have one (and I'm too lazy to check)
        newStyle.Setters.Add(new Setter(Image.RenderTransformProperty, new RotateTransform(0)));
        newStyle.Setters.Add(new Setter(Image.RenderTransformOriginProperty, new Point(0.5, 0.5)));

        var trigger = new DataTrigger();

        var binding = new Binding();

        var relativeSource = new RelativeSource();
        relativeSource.Mode = RelativeSourceMode.FindAncestor;
        relativeSource.AncestorType = typeof(Button);

        binding.RelativeSource = relativeSource;
        binding.Path = new PropertyPath(Button.VisibilityProperty);

        trigger.Binding = binding;
        trigger.Value = Visibility.Visible;

        var storyboard = new Storyboard();

        var animation = new DoubleAnimationUsingKeyFrames();
        animation.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("(0).(1)", Image.RenderTransformProperty, RotateTransform.AngleProperty));
        animation.Duration = new Duration(TimeSpan.FromSeconds(5)); // spec said 30, but i wanted to actually see it happen!
        animation.KeyFrames.Add(new LinearDoubleKeyFrame(-12, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2))));
        animation.KeyFrames.Add(new LinearDoubleKeyFrame(12, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.4))));
        animation.KeyFrames.Add(new LinearDoubleKeyFrame(0, KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.5))));

        storyboard.Children.Add(animation);
        storyboard.RepeatBehavior = RepeatBehavior.Forever;

        var beginStoryboard = new BeginStoryboard();
        beginStoryboard.Storyboard = storyboard;
        beginStoryboard.Name = "its_wobble_time"; // it is

        trigger.EnterActions.Add(beginStoryboard);

        var removeStoryboard = new RemoveStoryboard();
        removeStoryboard.BeginStoryboardName = beginStoryboard.Name;

        trigger.ExitActions.Add(removeStoryboard);

        newStyle.Triggers.Add(trigger);

        image.Style = newStyle;
    }
}

下面是它如何被使用:

<Button Width="100" Height="25" >
        <Image Source="Untitled.png" xmlns:local="clr-namespace:WpfApplication17" local:Wibble.Wobble="True" />
    </Button>

这篇关于如何在按钮动画图像在WPF每30秒动摇?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆