How do I make an ellipse blink?

前端 未结 2 519
面向向阳花
面向向阳花 2021-01-01 03:31

I am trying to make a custom control in WPF. I want it to simulate the behavior of a LED that can blink.

There are three states to the control: On, Off, and Blinkin

相关标签:
2条回答
  • 2021-01-01 04:04

    You can do this with an animation that auto-reverses and repeats (this is for 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:

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

    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;    
            }
        }       
    }
    }
    
    0 讨论(0)
  • 2021-01-01 04:12

    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:

    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.

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

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

    0 讨论(0)
提交回复
热议问题