views:

1257

answers:

3

PROBLEM:

I have a custom class named BlinkingLight. I also have a static ObservableCollection BlinkingLightCollection. In the UI, I have a ListBox that is bound to BlinkingLightCollection.

In my ListBox I want to essentially display each BlinkingLight object as a custom control that looks like box with an LED light that has an animation that makes the LED look like it just flashed on for a second then goes back to normal.

My BlinkingLight class has third party "LED" object that raises an event called 'Flash'.

I am looking for ideas or solutions to get this to work!

MY FAILED ATTEMPT:

I created a custom control (BlinkingLightControl) that can bind to the data of my BlinkingLight class when a BlinkingLight is the DataContext of my custom control.

I created a DataTemplate for my ListBox:

<Window.Resources>
  <DataTemplate x:Key="blinkingLightItemTemplate" >
    <local:BlinkingLightControl />
  </DataTemplate>
</Window.Resources>

<ListBox ItemsSource={Binding Source={x:Static local:Data.BlinkingLightCollection}}
         ItemTemplate="{StaticResource blinkingLightItemTemplate}" />

Note: I can just put the xaml for my custom control into the datatemplate instead having a completely different control if that makes things easier.

Now I want to have an EventTrigger in my BlinkingLightControl (or DataTemplate) who's RoutedEvent is the LED.Flash event. Unfortunately I can't seem to figure this part out. I've tried to create a RoutedEvent in my BlinkingLight class and just raise it whenever I handle the LED.Flash event. However my class is not a UIElement or ContentElement, and per MSDN: MSND Link

"The routed event owner can be any class, but routed events must be raised by and handled by UIElement or ContentElement derived classes in order to be useful. For more information about custom events, see How to: Create a Custom Routed Event."

Any help would be greatly appreciated!! Thanks, Scott

+1  A: 

In this case, the best way is to use WPF Commanding and create a "BlinkTheLights" RoutedCommand - your BlinkingLightControl will handle the BlinkTheLights command, and respond by starting a StoryBoard which does the light blink.

Paul Betts
Thanks for the response! I haven't explored the use of commands in WPF yet, but I will give this a shot and let everyone know how it works!
Scott
A: 

I was able to come up with a solution that has worked quite well:

Since my DataTemplate simply contains a custom UserControl (which binds to the DataContext to get its data from the business object)... I placed my custom RoutedEvent in the UserControl. Then in my UserControl's loaded event, I cast the DataContext as my business object to get access to the business object's property that has the event and hook it up to an event handler. (in my example I cast the DataContext as a BlinkingLight object, then I can get access to its Led property's Flash event and hook it up to a custom event handler). Note: The LED object must be a property, not just a field in the BlinkingLight object for it to work.

Then the event handler can raise the UserControl's custom Routed Event (FlashGreenEvent). Below is the back end code that now supplements the code in the OP (I've stripped out any other irrelevant code).

public partial class BlinkingLightControl : UserControl
{
    public static readonly RoutedEvent FlashGreenEvent = EventManager.RegisterRoutedEvent("FlashGreen", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(BlinkingLightControl));
    public event RoutedEventHandler FlashGreen
    {
        add { AddHandler(FlashGreenEvent, value); }
        remove { RemoveHandler(FlashGreenEvent, value); }
    }

    private void BlinkingLightControl_Loaded(object sender, RoutedEventArgs e)
    {
        BlinkingLight blinkingLight = (BlinkingLight)this.DataContext;

        blinkingLight.Led.Flash += LED_Flash;
    }

    protected delegate void LED_FlashCallback(ThirdParty.LED sender);
    public void LED_Flash(ThirdParty.LED sender)
    {
        if (this.Dispatcher.CheckAccess())
        {
            // Raise the Flash Green Event;
            RaiseEvent(new RoutedEventArgs(BlinkingLightControl.FlashGreenEvent));
        }
        else
            this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new LED_FlashCallback(LED_Flash), sender);
    }
}
Scott
A: 

If you're making a custom control you could always set the trigger outside of the control template.

something like:

  <Style TargetType="{x:Type local:MyControl}">

  <!-- fade in the control with an animation -->
  <Style.Triggers>
    <EventTrigger RoutedEvent="Control.Loaded">
      <BeginStoryboard>
        <Storyboard>
         <DoubleAnimation To="1" Duration="0:0:1" Storyboard.TargetProperty="Opacity"/>
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Style.Triggers>

  <!-- Make sure the opacity starts at 0 -->
  <Setter Property="Opacity" Value="0"/>
  <Setter Property="Template">
      <Setter.Value>
         <ControlTemplate TargetType="{x:Type local:MyControl}">
         </ControlTemplate>
      </Setter.Value>
  </Setter>
</Style>
Andy