views:

1781

answers:

4

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

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

Can it be done with something like a timer or callback, with two ellipses whose visiblity alternates?

But I like Pax's answer too. :)

John Lockwood
Unfortunately, timers and callbacks are not the WPF way. Probably not a good approach to take.
MedicineMan
+3  A: 

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;    
  }
 }  
}
}
Michael S. Scherotter
I'm not seeing EasingColorKeyFrame. Is this a Silverlight solution? Also this.Blink.Begin() is not available to me. I am using WPF.
MedicineMan
That is a Silverlight Solution. I'll add another answer for WPF.
Michael S. Scherotter
+1  A: 

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

Scott
A: 

Hi did u find the solution for the blink in WPF. if fount do assist me

kel