如何让椭圆形闪烁?

5

我正在尝试在WPF中制作自定义控件。我希望它能模拟一个可以闪烁的LED的行为。

该控件有三种状态:开启、关闭和闪烁。

我知道如何通过代码设置开启和关闭,但这个WPF动画让我非常困扰!我无法使任何东西进行动画。计划是创建一个名为“state”的属性。当用户将值设置为“blinking”时,我希望控件在绿色和灰色之间交替显示。我认为我需要一个依赖属性,但不知道怎么做。 我以前有更多的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>

可以使用类似定时器或回调的东西来完成吗?两个省略号的可见性交替出现?但我也喜欢Pax的答案。 :) - John Lockwood
我猜你正在尝试在Visual Studio中进行动画制作 - 为此使用Expression Blend - 它具有用于创建动画的设计工具。 - Michael S. Scherotter
不幸的是,定时器和回调函数并不是 WPF 的方式。这可能不是一个好的方法。 - MedicineMan
我承认,在花费了一些时间后,没有取得任何进展并且面临最后期限时,我开始有点慌乱并且有些失去理智。我启动了Expression Blend,并发现你创建的故事板运行良好。我只需要进行一些微调就可以获得我想要的行为。 - MedicineMan
2个回答

9
你可以使用自动反向和重复的动画来实现这一点(适用于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>

在控件加载或属性设置时开始动画 - 除非你需要,否则不需要依赖属性。

private bool blinking;
public bool IsBlinking
{
    get
    {
       return blinking;
    }
    set
    {
        if (value)
        {
             this.Blink.Begin();
        }
        else
        {
             this.Blink.Stop();
        }

        this.blinking = value;
    }
}

或在启动时:

private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
    this.Blink.Begin();
}

以下是在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>

然后通过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;    
        }
    }       
}
}

1
我没有看到 EasingColorKeyFrame。这是 Silverlight 的解决方案吗?此外,this.Blink.Begin() 对我不可用。我正在使用 WPF。 - MedicineMan
那是一个Silverlight解决方案。我会为WPF添加另一个答案。 - Michael S. Scherotter
我想知道如何同时闪烁多个按钮,即使有些按钮被设置为稍后闪烁?目前我在代码后台使用计时器:bool flashNow = now.Second % 2 == 0; 是否有其他解决方案(在XAML中)? - J Pollack

5
为了更好地控制代码中的眨眼速率等内容,我建议在您的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); }
}

在代码后台,您可以设置一个定时器来触发事件,无论您想要多久(这也为您提供了机会,在您需要时仅闪烁灯光一次):

RaiseEvent(new RoutedEventArgs(LedControl.Blink));

现在在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>

另外,我直接引用了在ledControl中定义的椭圆“ledEllipse”及其相应的DropShadowEffect“glow”(其中redLight只是我在填充属性上使用的另一个径向渐变刷子):

<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>

注意:DropShadowEffect是在.Net 3.5中引入的,但是如果您不想要发光效果(但它在纯色对比背景上看起来很好),则可以将其删除。


你的颜色方案非常准确,值得点赞。相比之下,被接受的答案的颜色方案不如你的好。 - Jeff LaFay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接