Race condition when using a Binding on a DoubleAnimation inside of a Storyboard

Nice example of a race condition on MSDN Forums, because the Binding is running on a different thread and won’t update in time. Let’s look at the problem:

There’s this Storyboard in the MainWindow to animate the Rectangle with the name “rectangle”:

<Window.Resources>
  <local:DurationConverter x:Key="DurationConverter"></local:DurationConverter>
  <Storyboard x:Key="storyboard">
    <DoubleAnimation Storyboard.TargetProperty="Width" Storyboard.TargetName="rectangle" To="500"
                            Duration="{Binding Converter={StaticResource DurationConverter}}">
    </DoubleAnimation>
  </Storyboard>
</Window.Resources>
<Grid Background="Black">
  <Rectangle x:Name="rectangle" Width="100" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="White"
              MouseDown="rectangle_MouseDown"/>
</Grid>

In the codebehind-File the Storyboard is started in the rectangle_MouseDown-handler by calling the Begin-method on it:

private void rectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
  Storyboard up = (Storyboard)this.Resources["storyboard"];
  up.Begin();
}

The DurationConverter returns a Duration of one Minute:

class DurationConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
  {
    return new System.Windows.Duration(TimeSpan.FromMinutes(1));
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

When you click on the Rectangle, you’ll see that it is animated with the default duration of 1 second instead of using the duration of 1 minute. When you add breakpoint in the Convert-method of the DurationConverter, you’ll see that the Convert-method is called after the call to the Begin-method of the Storyboard. So it’s too late to grab the wanted Duration.

How can you solve it? Just use a little trick to delay the call of the Begin-method on the Storyboard by one millisecond. This will do it:

private async void rectangle_MouseDown(object sender, MouseButtonEventArgs e)
{
  Storyboard up = (Storyboard)this.Resources["storyboard"];
  await Task.Delay(1);
  up.Begin();
}

Share this post

Comments (4)

  • Ziya Reply

    I checked, it works.
    Another option may be to subscribe to TargetUpdated event of Duration to begin Storyboard.

    July 26, 2016 at 12:00 pm
  • Jorge Rann Reply

    Is it possible to “debug” a race condition using a debugger? Should one use logging to “debug” a race?

    August 24, 2016 at 11:54 am
    • Thomas Claudius Huber Reply

      Hi Jorge,
      it depends. :) If you own the code or if you have the debugging symbols, then you can set a breakpoint in the code and work with the Threading-Window to switch between threads and to find it out. Logging works as well.

      Thomas

      August 29, 2016 at 5:50 pm

Leave a Reply

Your email address will not be published. Required fields are marked *

*