views:

365

answers:

3

Is there a way to define an animation somewhere in xaml (eg. as a resource) once and then reuse it multiple times? I have a lot of independent brushes across differnt datatemplates that independently need to start the same kind of animation based on a datatrigger. Now since it seems that an animation has to define an Storyboard.TargetName and Storyboard.TargetProperty. This pretty much defeats the purpose of reusability. I would somehow like to declare "use this animation form the resource but apply it to another element this time".

To me this seems to be a rather basic, important and essential request and I am suprised that its not that straight forward to acomplish. Am I missing something here?

The same thing applies for triggers. Suppose I have a lot of differnt visual elements that all represent the same type of state using color animations. E.g. fade to green when "active" fade to "red" when "error" etc. The only difference between the visuals is their shape/visual tree the desired animation behavior is the same, they all have an element somewhere in their visual tree that has a property of type color. I think it is not hard to imagine how tedious it is to redefine the same animations and datatrigger sets over and over again. Every developer hates this. I desperately seek for an easier solution that doesn't require no (or at least very little) c# code behind.

What I have come up with so far is this:

Define the animations in a resource lik this (repeat this for all basic states that there are, like activating, active, inactive, error):

<ColorAnimationUsingKeyFrames x:Key="deactivatingColorAnimation" 
                    Storyboard.TargetProperty="Material.(MaterialGroup.Children)[0].Brush.(SolidColorBrush.Color)"                    
                    FillBehavior="HoldEnd" RepeatBehavior="Forever" AutoReverse="True">
      <ColorAnimationUsingKeyFrames.KeyFrames>
        <LinearColorKeyFrame KeyTime="00:00:00" Value="Gray"/>
        <LinearColorKeyFrame KeyTime="00:00:0.25" Value="Gray"/>
        <LinearColorKeyFrame KeyTime="00:00:0.5" Value="Gray" />
        <LinearColorKeyFrame KeyTime="00:00:0.75" Value="Gray" />
     </ColorAnimationUsingKeyFrames.KeyFrames>
</ColorAnimationUsingKeyFrames>

The use it in storyboard in the triggers (repeat this zillions of times for each state X each differnt stateviusal, always come up with a new name for the storyboard):

<DataTrigger Binding="{Binding SubstrateHolder.State}" Value="Deactivating">
        <DataTrigger.EnterActions>
            <BeginStoryboard x:Name="someStateVisualDeactivatingStoryboard">
                <Storyboard Storyboard.TargetName="someStateVisual">
                    <StaticResource ResourceKey="deactivatingColorAnimation" />
                </Storyboard>
            </BeginStoryboard>
        </DataTrigger.EnterActions>
        <DataTrigger.ExitActions>
            <RemoveStoryboard BeginStoryboardName="someStateVisualDeactivatingStoryboard" />
        </DataTrigger.ExitActions>
</DataTrigger>

You can easily imagine how much bloat XAML I have to repeatedly copy and paste for all those zillion DataTriggers.

It would be cool to define all this triggers once and apply it to different state visuals. How is something like this solved in WPF? Any tip?

+3  A: 

Could you try something like this?

  • Wrap all your current control templates with an invisible root element, e.g. a Border or a StackPanel, whose bounding box will cover the entire control.
  • Create a style or control template for this invisible box that contains all your triggers and animations.
  • Have the animations animate an arbitrary Color property on the invisible box.
  • In the visual trees for all your different controls, bind any properties you want to animate to the Color property on the invisible root element.
Dewb
+1  A: 

There doesn' t seem to be any good XAML only solution to this general proplem. I ended up writing my own attached properties that define all the animation behavior for a given element. something like this:

<DataTemplate>
   <!-- ...  -->
   <Rectangle Fill="Gray">
     <v:AnimationHelper.Animations>
        <v:StandardColorStateAnimation TargetColorProperty="(Rectangle.Fill).(SolidColorBrush.Color)" TraggetSateProperty={Binding State} />
     </v:AnimationHelper.Animations>
   </Rectangle>
<DataTemplate>

The rest (creating the animation etc.) is done in the codebehind.

bitbonk
Ya know I tried a bunch of different things to come up with a suggestion for this and it just wasn't possible without doing something in code. This is as good a solution as I think you're going to get. Hurray for WPF extensibility, right? :)
Drew Marsh
It might be a good idea to use the silverlight behavior API for this. Do you agree? IS it compatible with the standard .net 3.5 framework?
bitbonk
No it's not compatible, though they're supposed. Honestly I think you've found a great solution that leverages the WPF architecture in a very "natural" way and shouldn't second guess yourself at all here.
Drew Marsh
Sorry, it's late and I realized kinda mispoke there. First off, it's not that they're not compatible, it's just that they're not natively in .NET 3.5. They come with Blend right now via System.Windows.Interactivity. What you've done here is implement what has been referred to as the "Attached Behavior" pattern of WPF and that's how we accomplished the kinda stuff behaviors provide before System.Windows.Interactivity. :)
Drew Marsh
I don't really like my approach due to the lack of designability. What if a designer would like to completely redesign the animations? I gues the best I can do is parametries the attached property more and the designer would have to handedit the xaml. Do you agree?
bitbonk
A: 

I realize this issue is a bit dead at the time of this posting, but I did find a solution that requires very little code behind.

You can make a UserControl with custom properties(Scroll down to about Figure 8) that contains your rectangle as well as the animations and state triggers. This user control would define a public property such as status that would trigger the color change when altered.

The only code-behind required is to create your variables in code.

The user control can them be reused over and over again without rewriting the the XAML storyboards or data triggers.

Andres