Hi guys!
Today I want to let you know how synchronizing animations belonging to different threads on WPF. The sample application is downloadable here.

1. The Sample Application

The example I propose consists in a Main Window containing a button that allow you creating other windows that display blinking lamps. The proposed application looks like the following picture:

Syncrhonizing animations across threads

As you can see the “Lamps windows” are created clicking on “Create new window” button from the “Main Application Thread” window. Obviously each window runs in its own thread but all the lamps blink synchronously whatever the thread they belong to. The following pictures display the “blink” effect showing the lamp in two different times:

lamps on LampsWindowsInactive

2. The proposal solution

Some time ago I encountered for the first time the problem to synchronize blinking lamps across threads in an application I was developing. The first solution that comes in my mind was to create a “common” DependencyProperty on the main Application thread and binding all others thread to that property. However I discovered that binding across threads is forbidden in WPF! But I had in my mind that events can be handled across threads!

So I developed two classes:

BlinkControllerClassDiagram

  • BlinkAnimationControllerManager: this class implements the Singleton pattern and contains the BlinkOpacityProperty which is the “common” DependencyProperty we need to synchronize all threads animations. The constructor of this class associates to this property a DoubleAnimationUsingKeyFrames animation that consist in a double value switching in time from 0 to 1. The class also contains the BlinkOpacityChangedEvent event for notifying the change of value of the BlinkOpacityProperty.
  • BlinkAnimationController: an instance of this class is created and used in all threads needing to synchronize blinking. This class implements INotifyPropertyChange interface and exposes the BlinkOpacity, which can be used in xaml code for data binding. The constructor retrieves the unique instance of the BlinkAnimationControllerManager object, it handles the BlinkOpacityChangedEvent and it stores the calling DispatcherThread. Finally, in the event handler method, the exposed BlinkOpacity property value is changed using the calling thread and this is allowed because we are in the same thread in which the property was created.

3. Data Binding

Every “Lamps window” can now bind ‘something’ to the BlinkAnimationController’s BlinkOpacity property. For doing that first of all we have to define an ObjectDataProvider that contains the BlinkAnimationController instance:

<ObjectDataProvider x:Key="blinkerODP"
                            MethodName="GetInstance"
                            ObjectType="{x:Type mynamespace:BlinkAnimationController}"/>

The blinking lamp appearance is described by a ControlTemplate defined by Grid containing two borders, the first one has a gray background and the second one has the background of the templatebinding background. Finally the Grid DataContext is associated to the ObjectDataProvider and the Opacity of the second Border is binded to the BlinkOpacityProperty:

<ControlTemplate x:Key="buttonLampTemplate"
                 TargetType="{x:Type Button}">
   ...

   <Grid Width="125"
         Height="35"
         DataContext="{Binding Source={StaticResource blinkerODP}}">

      <Border x:Name="lampBorder"
              Background="LightGray"
              ... />
      </Border>

      <Border x:Name="lampBorder_Copy"
      	      Background="{TemplateBinding Background}"
              CornerRadius="5"
              BorderBrush="#FF1A1A1A"
              BorderThickness="1"
              Opacity="{Binding BlinkOpacity}">
	      ...
      </Border>

   </Grid>

</ControlTemplate>

Marzio.

Advertisements