none
"Cannot freeze this Storyboard timeline tree for use across threads"

    Question

  • Hi,

    I'm trying to create a button template and I've run into the following issue.
    When I try to reference brushes in my resource dictionary the application gives me the following error right on startup "Failed object initialization (ISupportInitialize.EndInit). Cannot freeze this Storyboard timeline tree for use across threads."

    Here is my code:

      <Style TargetType="Button">
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="Button">
              <Border x:Name="Border" CornerRadius="2" BorderThickness="1" Background="{StaticResource NormalBrush}" BorderBrush="{StaticResource NormalBorderBrush}">
                <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
              </Border>
                <ControlTemplate.Resources>
                    <Storyboard x:Key="MouseOverEnter">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="{Binding Path=Color, Source={StaticResource NormalBrush}}"/>
                            <SplineColorKeyFrame KeyTime="00:00:00.1000000" Value="{Binding Path=Color, Source={StaticResource DarkBrush}}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                    <Storyboard x:Key="MouseOverLeave">
                        <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Border" Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">
                            <SplineColorKeyFrame KeyTime="00:00:00" Value="{Binding Path=Color, Source={StaticResource DarkBrush}}"/>
                            <SplineColorKeyFrame KeyTime="00:00:00.1000000" Value="{Binding Path=Color, Source={StaticResource NormalBrush}}"/>
                        </ColorAnimationUsingKeyFrames>
                    </Storyboard>
                </ControlTemplate.Resources>
              <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="true">
                    <Trigger.EnterActions>
                        <BeginStoryboard Storyboard="{StaticResource MouseOverEnter}"/>
                    </Trigger.EnterActions>
                    <Trigger.ExitActions>
                        <BeginStoryboard Storyboard="{StaticResource MouseOverLeave}"/>
                    </Trigger.ExitActions>
                </Trigger>
              </ControlTemplate.Triggers>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>

    when I change the storyboards to use static colors e.g.:
                        <Storyboard x:Key="MouseOverEnter">
                            <ColorAnimationUsingKeyFrames ...>
                                <SplineColorKeyFrame KeyTime="00:00:00" Value="AliceBlue"/>
                                <SplineColorKeyFrame KeyTime="00:00:00.1000000" Value="Chocolate"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                        <Storyboard x:Key="MouseOverLeave">
                            <ColorAnimationUsingKeyFrames ...>
                                <SplineColorKeyFrame KeyTime="00:00:00" Value="Chocolate"/>
                                <SplineColorKeyFrame KeyTime="00:00:00.1000000" Value="AliceBlue"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
    everything works just fine.

    How could I fix this?
    Thank you.
    Sunday, March 30, 2008 10:04 PM

Answers

  • Resources within (or used by) a control template need to be frozen so that the template can be shared across control instances and inflated very quickly whenever it is needed.  As soon as you set a binding on a dependency property of a Freezable, you prevent the Freezable from being frozen.  Thus, the bindings on the Value property of your SplineColorKeyFrame objects are preventing the storyboards from being frozen, which thereby prevents them from being accessed within a control template.

     

    You can get rid of these bindings and still have a resource-based approach if you simply declare static color resources along with static brush resources, as follows:

     

    Code Snippet

     

    <Color x:Key="NormalColor">AliceBlue</Color>

    <Color x:Key="DarkColor">Chocolate</Color>

    <SolidColorBrush x:Key="NormalBrush" Color="{StaticResource NormalColor}" />

    <SolidColorBrush x:Key="DarkBrush" Color="{StaticResource DarkColor}" />

     

     

    Then you can define the storyboards in your template like this:

     

    Code Snippet

     

    <Storyboard x:Key="MouseOverEnter">

      <ColorAnimationUsingKeyFrames BeginTime="00:00:00"

          Storyboard.TargetName="Border"

          Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">

        <SplineColorKeyFrame KeyTime="00:00:00"

            Value="{StaticResource NormalColor}"/>

        <SplineColorKeyFrame KeyTime="00:00:00.1000000"

            Value="{StaticResource DarkColor}"/>

      </ColorAnimationUsingKeyFrames>

    </Storyboard>

    <Storyboard x:Key="MouseOverLeave">

      <ColorAnimationUsingKeyFrames BeginTime="00:00:00"

          Storyboard.TargetName="Border"

          Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">

        <SplineColorKeyFrame KeyTime="00:00:00"

            Value="{StaticResource DarkColor}"/>

        <SplineColorKeyFrame KeyTime="00:00:00.1000000"

            Value="{StaticResource NormalColor}"/>

      </ColorAnimationUsingKeyFrames>

    </Storyboard>

     

     

    Sunday, March 30, 2008 10:53 PM

All replies

  • Resources within (or used by) a control template need to be frozen so that the template can be shared across control instances and inflated very quickly whenever it is needed.  As soon as you set a binding on a dependency property of a Freezable, you prevent the Freezable from being frozen.  Thus, the bindings on the Value property of your SplineColorKeyFrame objects are preventing the storyboards from being frozen, which thereby prevents them from being accessed within a control template.

     

    You can get rid of these bindings and still have a resource-based approach if you simply declare static color resources along with static brush resources, as follows:

     

    Code Snippet

     

    <Color x:Key="NormalColor">AliceBlue</Color>

    <Color x:Key="DarkColor">Chocolate</Color>

    <SolidColorBrush x:Key="NormalBrush" Color="{StaticResource NormalColor}" />

    <SolidColorBrush x:Key="DarkBrush" Color="{StaticResource DarkColor}" />

     

     

    Then you can define the storyboards in your template like this:

     

    Code Snippet

     

    <Storyboard x:Key="MouseOverEnter">

      <ColorAnimationUsingKeyFrames BeginTime="00:00:00"

          Storyboard.TargetName="Border"

          Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">

        <SplineColorKeyFrame KeyTime="00:00:00"

            Value="{StaticResource NormalColor}"/>

        <SplineColorKeyFrame KeyTime="00:00:00.1000000"

            Value="{StaticResource DarkColor}"/>

      </ColorAnimationUsingKeyFrames>

    </Storyboard>

    <Storyboard x:Key="MouseOverLeave">

      <ColorAnimationUsingKeyFrames BeginTime="00:00:00"

          Storyboard.TargetName="Border"

          Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)">

        <SplineColorKeyFrame KeyTime="00:00:00"

            Value="{StaticResource DarkColor}"/>

        <SplineColorKeyFrame KeyTime="00:00:00.1000000"

            Value="{StaticResource NormalColor}"/>

      </ColorAnimationUsingKeyFrames>

    </Storyboard>

     

     

    Sunday, March 30, 2008 10:53 PM
  • Thank you for your answer.
    It does work the way the it should but I would have hoped a more elegant solution was possible.
    Monday, March 31, 2008 4:23 AM
  • Just found this post ... and it very closely matches into a question I want to ask:
    If I change the StaticResource(s) into DynamicResource(s) inside the Storyboard  for skinning reasons ... I get the same old 'Cannot freeze this Storyboard timeline tree for use across threads' error.

    Any workaround for this?


    Basically, I am trying to skin a control that I would like to update at runtime with a ResourceDictionary swap. However, I need DynamicResource(s) for that ... but DynamicResource(s) doesn't seem to want to live in a Storyboard that lives in a ControlTemplate.
    Thursday, September 25, 2008 9:23 PM
  • Cory, can you post a sample that demonstrates your scenario?  It would help to see both the resources that you are referencing (the ones you want to swap out) as well as the elements where they are referenced.

    Chances are there is a solution, but it may not be as convenient (or as modular) as you'd like.  When implementing a resource dictionary swapping routine, it is often necessary to redefine a lot of the resources in both dictionaries.
    Dr. WPF - Online Office at http://drwpf.com/blog/
    Friday, September 26, 2008 9:58 PM
  • Dr. WPF,

    Ok, below is a piece of sample code ... I've cut a lot of the code out and hopefully have left the important parts in.

    If you replace the StaticResource(s) inside the Storyboard with DynamicResource ... I get the runtime error that I mentioned above. And, of course, if I don't have the DynamicResource(s) in, they don't swap when I change the skin via a menu item.

    The mechanism that I am using to change skins is pretty much the same mechanism that Family.Show uses (that's where I copied it from).

    Let me know if the code snippet below is not enough.

    Thanks for taking a look at it!
    Cory

    p.s.

    Just so you know, I am aware of the following. I am just hoping there is some sort of workaround.

    From http://msdn.microsoft.com/en-us/library/ms742868.aspx:
    You can't use dynamic resource references or data binding expressions to set Storyboard or animation property values. That's because everything inside a Style must be thread-safe, and the timing system must Freeze Storyboard objects to make them thread-safe. A Storyboard cannot be frozen if it or its child timelines contain dynamic resource references or data binding expressions. For more information about freezing and other Freezable features, see the Freezable Objects Overview.

    <UserControl> 
        <UserControl.Resources> 
            <!-- buttonStyle --> 
            <Style x:Key="buttonStyle" TargetType="{x:Type RepeatButton}"
                <Setter Property="Template"
                    <Setter.Value> 
                        <ControlTemplate TargetType="{x:Type RepeatButton}"
                            <ControlTemplate.Resources> 
                                <Storyboard x:Key="mouseOverEnterStoryboard"
                                    <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="innerGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)"
                                        <SplineColorKeyFrame KeyTime="00:00:00.1250000" Value="{StaticResource lightOrangeColor}"/> 
                                    </ColorAnimationUsingKeyFrames> 
                                    <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="innerGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)"
                                        <SplineColorKeyFrame KeyTime="00:00:00.1250000" Value="{StaticResource darkOrangeColor}"/> 
                                    </ColorAnimationUsingKeyFrames> 
                                    <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="outerGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)"
                                        <SplineColorKeyFrame KeyTime="00:00:00.1250000" Value="{StaticResource whiteColor}"/> 
                                    </ColorAnimationUsingKeyFrames> 
                                    <ColorAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="outerGradient" Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)"
                                        <SplineColorKeyFrame KeyTime="00:00:00.1250000" Value="{StaticResource leftSideColor}"/> 
                                    </ColorAnimationUsingKeyFrames> 
                                </Storyboard> 
                            </ControlTemplate.Resources> 
                            <Canvas 
                                x:Name="buttonCanvas" 
                                Width="25" 
                                Height="24" 
                                Canvas.Left="0" 
                                Canvas.Top="0" 
                            > 
                                <Path 
                                    x:Name="outerGradient" 
                                    Width="25" 
                                    Height="24" 
                                    Canvas.Left="0" 
                                    Canvas.Top="0" 
                                    Stretch="Fill" 
                                    Data=""
                                    <Path.Fill> 
                                        <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1"
                                            <LinearGradientBrush.GradientStops> 
                                                <GradientStop Color="{DynamicResource whiteColor}" Offset="0"/> 
                                                <GradientStop Color="{DynamicResource leftSideColor}" Offset="1"/> 
                                            </LinearGradientBrush.GradientStops> 
                                        </LinearGradientBrush> 
                                    </Path.Fill> 
                                </Path> 
                                <Path 
                                    x:Name="innerGradient" 
                                    Width="18.944" 
                                    Height="11.9684" 
                                    Canvas.Left="2.97935" 
                                    Canvas.Top="9.39318" 
                                    Stretch="Fill" 
                                    Data="" 
                                > 
                                    <Path.Fill> 
                                        <LinearGradientBrush StartPoint="0.5,1" EndPoint="0.5,0"
                                            <LinearGradientBrush.GradientStops> 
                                                <GradientStop Color="{DynamicResource whiteColor}" Offset="0"/> 
                                                <GradientStop Color="{DynamicResource leftSideColor}" Offset="1"/> 
                                            </LinearGradientBrush.GradientStops> 
                                        </LinearGradientBrush> 
                                    </Path.Fill> 
                                </Path> 
                                <Path 
                                    x:Name="arrow" 
                                    Width="11.1813" 
                                    Height="8.68533" 
                                    Canvas.Left="6.68468" 
                                    Canvas.Top="12.6763" 
                                    Stretch="Fill" 
                                    Fill="{DynamicResource darkGrayBrush}" 
                                    Data="" 
                                /> 
                                <Path 
                                    x:Name="innerBevel" 
                                    Width="20.4453" 
                                    Height="13.4681" 
                                    Canvas.Left="2.22855" 
                                    Canvas.Top="8.64349" 
                                    Stretch="Fill" 
                                    StrokeThickness="1.5" 
                                    StrokeLineJoin="Round" 
                                    Stroke="{DynamicResource lightGrayBrush}" 
                                    Data="" 
                                /> 
                                <Path 
                                    x:Name="outerBevel" 
                                    Width="26.5" 
                                    Height="25.5" 
                                    Canvas.Left="-0.749993" 
                                    Canvas.Top="-0.749998" 
                                    Stretch="Fill" 
                                    StrokeThickness="1.5" 
                                    StrokeMiterLimit="2.75" 
                                    Stroke="{DynamicResource darkGrayBrush}" 
                                    Data="" 
                                /> 
                            </Canvas> 
                            <ControlTemplate.Triggers> 
                                <Trigger Property="IsMouseOver" Value="True"
                                    <Trigger.EnterActions> 
                                        <BeginStoryboard x:Name="isMouseOverEnterBeginStoryboard" Storyboard="{StaticResource mouseOverEnterStoryboard}"/> 
                                    </Trigger.EnterActions> 
                            </ControlTemplate.Triggers> 
                        </ControlTemplate> 
                    </Setter.Value> 
                </Setter> 
            </Style> 
        </UserControl.Resources> 
     
        <Grid> 
            <RepeatButton 
                x:Name="button" 
                Style="{DynamicResource buttonStyle}" 
                Width="26.5" 
                Height="25.5" 
            /> 
        </Grid> 
    </UserControl> 
     

    Saturday, September 27, 2008 12:24 AM
  • Cory Plotts said:
    <Path 
        x:Name="outerGradient" 
        Width="25" 
        Height="24" 
        Canvas.Left="0" 
        Canvas.Top="0" 
        Stretch="Fill" 
        Data=""


    You don't trust us with your path data?  ;-)

    Seriously, though....  I honestly don't see a way to make the template in this style swappable unless you move the entire "buttonStyle" resource into the dictionary that is being swapped.  More precisely, you would need the entire buttonStyle resource defined in both dictionaries (rather than in the Resources of the UserControl).  I realize this doesn't make it as self-contained, but it already crosses that boundary by referencing resources (like "lightOrangeColor") that exist outside the user control.
    Dr. WPF - Online Office at http://drwpf.com/blog/
    • Edited by Dr. WPF Monday, September 29, 2008 8:46 PM typo
    • Proposed as answer by Cory Plotts Tuesday, September 30, 2008 5:17 PM
    Monday, September 29, 2008 8:45 PM
  • Dr. WPF said:

    You don't trust us with your path data?  ;-)

    No, it is a secret, never-done before, if I tell you, you know ... I'd have to ... heh heh. No, seriously, I was just trying to keep the post size to a minimum and hopefully legible size.

    Your suggestion is a great one and exactly the type of workaround I was looking for. I don't know why I didn't think of it myself, actually.

    Thanks!
    Cory


    Tuesday, September 30, 2008 5:17 PM
  • Dr WPF,

    I keep hitting a similar issue whenever I want to color animate something from "some colour" to "whatever it was before". Consider the Xaml below.

    What I'm trying to achieve, is to fade from Yellow, back to the existing background colour of the textbox. It could be white if it's active, or grey if it's read only. I've also hit this issue on things like highlighting a row in a grid with alternate row colours. Depending on the colour of the row, the colourI fade back to will be different.

    However, as for the posters above, I get the "Cannot freeze this Storyboard timeline tree for use across threads". I can't really put the "To" value as a static resource as I won't know what it will be ahead of time.

    Any ideas?

     <Style x:Key="highlightChangeInput" BasedOn="{StaticResource placeOrderInput}" > 
            <Style.Triggers> 
                <EventTrigger RoutedEvent="TextBox.TextChanged">  
                    <EventTrigger.Actions> 
                        <BeginStoryboard> 
                            <Storyboard Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)">  
                                <!-- This will blow up -->
                                <ColorAnimation From="Yellow" To="{Binding Source={RelativeSource Self}, Path=(Control.Background).(SolidColorBrush.Color)}" Duration="0:0:2"/>  
                            </Storyboard> 
                        </BeginStoryboard> 
                    </EventTrigger.Actions> 
                </EventTrigger> 
            </Style.Triggers> 
        </Style> 
    Wednesday, October 22, 2008 5:29 AM
  • I have a similar problem to stevens, where I have binding to a freezable in my style, and I can't factor it out to a static resource because it changes at run-time. Any ideas?
    Monday, March 09, 2009 10:15 PM
  • I am also trying to do exactly the same as steven_pack.

    Things are not looking good for animations + binding...
    Tuesday, October 06, 2009 12:46 AM
  • Just want to add a comment: I was trying to do the same thing, make my color animation value dynamic in an animation and ran into the same error.

    However, I didn't really need it to be dynamic at runtime, more dynamic at design time for WinForm developers consuming my WPF user control (.dll) . . . it was just my first instinct to try to expose a dependency property bound to the animation's color value which WinForm developers could set dynamically during a Form load event . . . before making the WPF control the child of an ElementHost.

    I needed a means to make the colors used in the animation exposed and configurable. This does appear to be possible using a resource dictionary merged in via the pack siteoforigin syntax. Such "loose" resources can apparently be a "StaticResource" since they won't change after the initial loading of the window? It's a subtle point. I wanted to distribute a .dll to WinForm developers with an API they could easily manipulate when embedding my WPF control in an Element Host. I was able to allow WinForm developers to configure my control by distributing the loose xaml file with the .dll. Not bad . . .

    I figure there might be others who read this post who might be looking for this different, unique goal: more dynamic-at-post-compile-time vs. dynamic-at-runtime.

     

     

    <ResourceDictionary.MergedDictionaries>

     

     

    <ResourceDictionary Source="pack://siteoforigin:,,,/DifferentColorsForAnimations.xaml"></ResourceDictionary>

     

     

    </ResourceDictionary.MergedDictionaries>

     

     

    <Storyboard x:Key="OnMouseEnterStoryboard">

     

     

    <ColorAnimationUsingKeyFrames AutoReverse="False" BeginTime="00:00:00"

     

    Storyboard.TargetProperty="(Panel.Background).(GradientBrush.GradientStops)[1].(GradientStop.Color)">

     

     

    <SplineColorKeyFrame KeyTime="00:00:01" Value="{StaticResource MouseOverBottomColor}"/>

     

     

    </ColorAnimationUsingKeyFrames>

     

     

    </Storyboard>

    Saturday, October 10, 2009 12:20 AM