I am trying to make a custom control in WPF. I want it to simulate the behavior of a LED that can blink.
我试图在WPF中进行自定义控件。我希望它模拟可以闪烁的LED的行为。
There are three states to the control: On, Off, and Blinking.
控件有三种状态:开,关和闪烁。
I know how to set On and Off through the code behind, but this WPF animation stuff is just driving me nuts!!!! I cannot get anything to animate whatsoever. The plan is to have a property called state. When the user sets the value to blinking, I want the control to alternate between green and grey. I'm assuming I need a dependency property here, but have no idea. I had more xaml before but just erased it all. it doesn't seem to do anything. I'd love to do this in the most best practice way possible, but at this point, I'll take anything. I'm half way to writing a thread that changes the color manually at this point.
我知道如何通过后面的代码设置On和Off,但是这个WPF动画的东西只是让我疯了!我无法获得任何动画效果。计划是拥有一个名为state的财产。当用户将值设置为闪烁时,我希望控件在绿色和灰色之间切换。我假设我需要一个依赖属性,但不知道。之前我有更多的xaml,但只是删除了所有。它似乎没有做任何事情。我希望以尽可能最好的方式做到这一点,但在这一点上,我会采取任何措施。我正在写一个在这一点上手动改变颜色的线程的一半。
<UserControl x:Class="WpfAnimation.LED"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<Ellipse x:Name="MyLight" Height="Auto" Width="Auto"/>
</Grid>
</UserControl>
2 个解决方案
#1
7
You can do this with an animation that auto-reverses and repeats (this is for Silverlight):
您可以使用自动反转和重复的动画执行此操作(这适用于Silverlight):
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Blinker.MainPage"
Width="640" Height="480" Loaded="UserControl_Loaded">
<UserControl.Resources>
<Storyboard x:Name="Blink" AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ellipse"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01" Value="Gray"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Ellipse x:Name="ellipse" Fill="Green" Stroke="Black"/>
</Grid>
</UserControl>
and then start the animation when the control loads or when a property is set - you don't need a dependency property unless you
然后在控件加载或设置属性时启动动画 - 除非你不需要依赖属性
private bool blinking;
public bool IsBlinking
{
get
{
return blinking;
}
set
{
if (value)
{
this.Blink.Begin();
}
else
{
this.Blink.Stop();
}
this.blinking = value;
}
}
or on startup:
或在启动时:
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
this.Blink.Begin();
}
Here is another way to do it in WPF - using the VisualStateManager - that will also work in Silverlight:
这是另一种在WPF中使用VisualStateManager的方法 - 它也适用于Silverlight:
<UserControl
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"
mc:Ignorable="d"
x:Class="BlinkerApp.Blinker"
x:Name="UserControl"
d:DesignWidth="100" d:DesignHeight="100">
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="BlinkStates">
<VisualState x:Name="Blinking">
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:01" Value="Gray"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Stopped"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="ellipse" Fill="Green" Stroke="Black"/>
</Grid>
and then have the IsBlinking property switch the visual state:
然后让IsBlinking属性切换视觉状态:
namespace BlinkerApp
{
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// Interaction logic for Blinker.xaml
/// </summary>
public partial class Blinker : UserControl
{
private bool blinking;
public Blinker()
{
this.InitializeComponent();
}
public bool IsBlinking
{
get
{
return blinking;
}
set
{
if (value)
{
VisualStateManager.GoToState(this, "Blinking", true);
}
else
{
VisualStateManager.GoToState(this, "Stopped", true);
}
this.blinking = value;
}
}
}
}
#2
4
To allow for greater control of the blink rate and such in your code behind, I'd suggest having a routed event in your UserControl called Blink:
为了更好地控制眨眼率等等,我建议你在UserControl中使用一个名为Blink的路由事件:
public static readonly RoutedEvent BlinkEvent = EventManager.RegisterRoutedEvent("Blink", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(LedControl));
public event RoutedEventHandler Blink
{
add { AddHandler(BlinkEvent, value); }
remove { RemoveHandler(BlinkEvent, value); }
}
In your code behind you can set up a timer to raise the event however often you like (this also gives you the opportunity to blink the light a single time whenever you want:
在你的代码背后,你可以设置一个计时器来提升你喜欢的事件(这也让你有机会在你想要的时候一次闪烁灯光:
RaiseEvent(new RoutedEventArgs(LedControl.Blink));
Now in XAML, the following code would make a glow visible, and set the fill property of your ellipse (ledEllipse) to a bright green radial gradient, then return the fill value to a dim 'unlit' green (which you could change to gray if you like). You can simply change the duration to make the blink last longer.
现在在XAML中,以下代码将使光晕可见,并将椭圆的填充属性(ledEllipse)设置为亮绿色径向渐变,然后将填充值返回到暗淡的“未点亮”绿色(可以将其更改为灰色)如果你喜欢)。您只需更改持续时间即可使眨眼持续更长时间。
<UserControl.Triggers>
<EventTrigger RoutedEvent="local:LedControl.Blink">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="glow"
Storyboard.TargetProperty="Opacity"
To="100"
AutoReverse="True"
Duration="0:0:0.075" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ledEllipse"
Storyboard.TargetProperty="Fill"
Duration="0:0:0.15">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.01">
<DiscreteObjectKeyFrame.Value>
<RadialGradientBrush>
<!--bright Green Brush-->
<GradientStop Color="#FF215416" Offset="1"/>
<GradientStop Color="#FE38DA2E" Offset="0"/>
<GradientStop Color="#FE81FF79" Offset="0.688"/>
</RadialGradientBrush>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.15" >
<DiscreteObjectKeyFrame.Value>
<RadialGradientBrush>
<!--dim Green Brush-->
<GradientStop Color="#FF21471A" Offset="1"/>
<GradientStop Color="#FF33802F" Offset="0"/>
<GradientStop Color="#FF35932F" Offset="0.688"/>
</RadialGradientBrush>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</UserControl.Triggers>
Also, I am directly referencing the ellipse 'ledEllipse' and it's corresponding DropShadowEffect 'glow' which are defined in the ledControl as following (redLight is just another radial gradient brush that I start my led's fill property at):
另外,我直接引用椭圆'ledEllipse'并且它对应的DropShadowEffect'glow'在ledControl中定义如下(redLight只是另一个径向渐变画笔,我开始我的led的填充属性):
<Ellipse x:Name="statusLight" Height="16" Width="16" Margin="0" Fill="{DynamicResource redLight}" >
<Ellipse.Effect>
<DropShadowEffect x:Name="glow" ShadowDepth="0" Color="Lime" BlurRadius="10" Opacity="0" />
</Ellipse.Effect>
</Ellipse>
Note: The DropShadowEffect was introduced in .Net 3.5, but you could remove that if you don't want a glow effect (but it looks nice on a solid color contrasting background).
注意:DropShadowEffect是在.Net 3.5中引入的,但是如果你不想要一个发光效果你可以删除它(但它在纯色对比背景上看起来不错)。
#1
7
You can do this with an animation that auto-reverses and repeats (this is for Silverlight):
您可以使用自动反转和重复的动画执行此操作(这适用于Silverlight):
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Blinker.MainPage"
Width="640" Height="480" Loaded="UserControl_Loaded">
<UserControl.Resources>
<Storyboard x:Name="Blink" AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="ellipse"
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="00:00:01" Value="Gray"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Ellipse x:Name="ellipse" Fill="Green" Stroke="Black"/>
</Grid>
</UserControl>
and then start the animation when the control loads or when a property is set - you don't need a dependency property unless you
然后在控件加载或设置属性时启动动画 - 除非你不需要依赖属性
private bool blinking;
public bool IsBlinking
{
get
{
return blinking;
}
set
{
if (value)
{
this.Blink.Begin();
}
else
{
this.Blink.Stop();
}
this.blinking = value;
}
}
or on startup:
或在启动时:
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
this.Blink.Begin();
}
Here is another way to do it in WPF - using the VisualStateManager - that will also work in Silverlight:
这是另一种在WPF中使用VisualStateManager的方法 - 它也适用于Silverlight:
<UserControl
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"
mc:Ignorable="d"
x:Class="BlinkerApp.Blinker"
x:Name="UserControl"
d:DesignWidth="100" d:DesignHeight="100">
<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="BlinkStates">
<VisualState x:Name="Blinking">
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
<SplineColorKeyFrame KeyTime="00:00:01" Value="Gray"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Stopped"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="ellipse" Fill="Green" Stroke="Black"/>
</Grid>
and then have the IsBlinking property switch the visual state:
然后让IsBlinking属性切换视觉状态:
namespace BlinkerApp
{
using System.Windows;
using System.Windows.Controls;
/// <summary>
/// Interaction logic for Blinker.xaml
/// </summary>
public partial class Blinker : UserControl
{
private bool blinking;
public Blinker()
{
this.InitializeComponent();
}
public bool IsBlinking
{
get
{
return blinking;
}
set
{
if (value)
{
VisualStateManager.GoToState(this, "Blinking", true);
}
else
{
VisualStateManager.GoToState(this, "Stopped", true);
}
this.blinking = value;
}
}
}
}
#2
4
To allow for greater control of the blink rate and such in your code behind, I'd suggest having a routed event in your UserControl called Blink:
为了更好地控制眨眼率等等,我建议你在UserControl中使用一个名为Blink的路由事件:
public static readonly RoutedEvent BlinkEvent = EventManager.RegisterRoutedEvent("Blink", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(LedControl));
public event RoutedEventHandler Blink
{
add { AddHandler(BlinkEvent, value); }
remove { RemoveHandler(BlinkEvent, value); }
}
In your code behind you can set up a timer to raise the event however often you like (this also gives you the opportunity to blink the light a single time whenever you want:
在你的代码背后,你可以设置一个计时器来提升你喜欢的事件(这也让你有机会在你想要的时候一次闪烁灯光:
RaiseEvent(new RoutedEventArgs(LedControl.Blink));
Now in XAML, the following code would make a glow visible, and set the fill property of your ellipse (ledEllipse) to a bright green radial gradient, then return the fill value to a dim 'unlit' green (which you could change to gray if you like). You can simply change the duration to make the blink last longer.
现在在XAML中,以下代码将使光晕可见,并将椭圆的填充属性(ledEllipse)设置为亮绿色径向渐变,然后将填充值返回到暗淡的“未点亮”绿色(可以将其更改为灰色)如果你喜欢)。您只需更改持续时间即可使眨眼持续更长时间。
<UserControl.Triggers>
<EventTrigger RoutedEvent="local:LedControl.Blink">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="glow"
Storyboard.TargetProperty="Opacity"
To="100"
AutoReverse="True"
Duration="0:0:0.075" />
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ledEllipse"
Storyboard.TargetProperty="Fill"
Duration="0:0:0.15">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.01">
<DiscreteObjectKeyFrame.Value>
<RadialGradientBrush>
<!--bright Green Brush-->
<GradientStop Color="#FF215416" Offset="1"/>
<GradientStop Color="#FE38DA2E" Offset="0"/>
<GradientStop Color="#FE81FF79" Offset="0.688"/>
</RadialGradientBrush>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame KeyTime="0:0:0.15" >
<DiscreteObjectKeyFrame.Value>
<RadialGradientBrush>
<!--dim Green Brush-->
<GradientStop Color="#FF21471A" Offset="1"/>
<GradientStop Color="#FF33802F" Offset="0"/>
<GradientStop Color="#FF35932F" Offset="0.688"/>
</RadialGradientBrush>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</UserControl.Triggers>
Also, I am directly referencing the ellipse 'ledEllipse' and it's corresponding DropShadowEffect 'glow' which are defined in the ledControl as following (redLight is just another radial gradient brush that I start my led's fill property at):
另外,我直接引用椭圆'ledEllipse'并且它对应的DropShadowEffect'glow'在ledControl中定义如下(redLight只是另一个径向渐变画笔,我开始我的led的填充属性):
<Ellipse x:Name="statusLight" Height="16" Width="16" Margin="0" Fill="{DynamicResource redLight}" >
<Ellipse.Effect>
<DropShadowEffect x:Name="glow" ShadowDepth="0" Color="Lime" BlurRadius="10" Opacity="0" />
</Ellipse.Effect>
</Ellipse>
Note: The DropShadowEffect was introduced in .Net 3.5, but you could remove that if you don't want a glow effect (but it looks nice on a solid color contrasting background).
注意:DropShadowEffect是在.Net 3.5中引入的,但是如果你不想要一个发光效果你可以删除它(但它在纯色对比背景上看起来不错)。