2009-09-22 19 views
5

Estoy intentando hacer un control personalizado en WPF. Quiero que simule el comportamiento de un LED que puede parpadear.¿Cómo puedo hacer que parpadee una elipse?

Hay tres estados para el control: encendido, apagado y parpadeo.

Sé cómo activar y desactivar el código de detrás, pero este material de animación de WPF me está volviendo loco !!!! No puedo obtener nada para animar en absoluto. El plan es tener una propiedad llamada estado. Cuando el usuario configura el valor para que parpadee, quiero que el control alterne entre verde y gris. Supongo que necesito una propiedad de dependencia aquí, pero no tengo idea. Tuve más xaml antes, pero simplemente lo borré todo. no parece hacer nada. Me encantaría hacer esto de la mejor manera posible, pero en este punto, tomaré cualquier cosa. Estoy a medio camino de escribir un hilo que cambie el color manualmente en este punto.

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

Supongo que estás tratando de hacer animación en Visual Studio - Use Expression Blend para eso - tiene herramientas de diseño para crear animaciones. –

+0

Yo soy. Debo admitir que después de pasar algo de tiempo en esto, no llegar a ningún lado, y enfrentarme a una fecha límite, comencé a ponerme un poco frenético y perdí un poco la cabeza. Activé Expression Blend y vi que el guión gráfico que creaste funcionaba bien. Solo necesitaba hacer algunos pequeños ajustes para obtener el comportamiento que quiero. – MedicineMan

+0

¿Se puede hacer con algo así como un temporizador o una devolución de llamada, con dos elipses cuya visibilidad se alterna? Pero me gusta la respuesta de Pax también. :) –

Respuesta

7

Usted puede hacer esto con una animación que se auto-reveses y repeticiones (esto es para 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> 

y luego iniciar la animación cuando las cargas de control o cuando una propiedad se establece - usted no necesita una propiedad de dependencia a menos que

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

     this.blinking = value; 
    } 
} 

o en el arranque:

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

Aquí hay otra manera de hacerlo en WPF - mediante el VisualStateManager - que también trabajará en 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> 

y luego tener la propiedad IsBlinking conmutar el estado visual:

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

No veo EasingColorKeyFrame. ¿Es esta una solución de Silverlight? Además this.Blink.Begin() no está disponible para mí. Estoy usando WPF. – MedicineMan

+0

Esa es una solución de Silverlight. Agregaré otra respuesta para WPF. –

+0

Me pregunto cómo hacer parpadear varios botones al mismo tiempo, incluso si algunos botones están configurados para parpadear más tarde. Actualmente utilizo el temporizador en el código detrás: bool flashNow = now.Second% 2 == 0; ¿Hay alguna otra solución (en XAML)? –

4

Para permitir un mayor control de la frecuencia de parpadeo y tal en su código detrás, le sugiero tener un evento enrutado en su UserControl llamado 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); } 
} 

En su código detrás de usted puede configurar un temporizador para provocar el evento sin embargo a menudo le gusta (esto también le da la oportunidad a parpadear la luz de una sola vez cuando lo desee:

RaiseEvent(new RoutedEventArgs(LedControl.Blink)); 

Ahora en XAML, el siguiente código haría visible un resplandor, y establecería la propiedad de relleno de su elipse (ledEllipse) en un degradado radial verde brillante, luego devolvería el valor de relleno a un verde oscuro apagado (que podría cambiar a gris si te gusta). Simplemente puede cambiar la duración para que el parpadeo dure más tiempo.

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

Además, estoy referencia directa a la elipse 'ledEllipse' y su correspondiente DropShadowEffect 'brillo' que se definen en el ledControl de la siguiente manera (Redlight es sólo otro pincel degradado radial que comienzo mi propiedad llenado de llevado a) :

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

Nota: El DropShadowEffect se introdujo en .Net 3.5, pero se puede quitar que si usted no quiere un efecto de brillo (pero se ve bien en un fondo de color de contraste sólido).

+0

+1 por ser perfecto con los colores. La respuesta aceptada no tiene tan buena combinación de colores como su solución. – jlafay

Cuestiones relacionadas