在处理任何与样式和动画有关的事情时,我并不擅长。
当按钮 Visibility 设置为 Visibility.Visible 时,我希望能够获得一些帮助,使图像是 Button 每 30 秒抖动一次的唯一内容。
这是为了引起用户的注意,鼓励他们点击按钮。
我更愿意将此作为 Image 上的附加行为,或者如果可能的话,甚至是 UIControl,以使其易于重用而不是弄乱样式,因为我已经在使用控件供应商提供的样式,并且我不想编辑它。
这是我使用的来自标记答案的解决方案
这是可以应用于任何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 中使用它。
<Button>
<Image Source="myImage.png">
<i:Interaction.Behaviors>
<local:ShakeBehavior RepeatInterval="30" SpeedRatio="3.0"/>
</i:Interaction.Behaviors>
</Image>
</Button>
有关附加行为的明确定义,您可以查看System.Windows.Interactivity.Behavior类注释。行为可以选择具有附加属性,也可以使它们非常有用。
有关附加属性的明确定义,您可以阅读 MSDN 的附加属性概述。附加属性可以做任何事情,它们可以被认为是附加行为,因为它们可以触发一个动作发生,从而导致一个有效的行为,但是从技术上讲,它们仍然只是一个附加属性。
由于附加属性可以像行为一样,人们也开始将这些类型的附加属性称为附加行为,而实际上它并不是真正的附加行为,除非您从 Behavior 派生到附加属性Interaction.Behaviors集合.
与 WPF/Silverlight 中的大多数内容一样,任何附加行为或附加属性都不需要混合。