define animations and triggers as reusable resource?

让人想犯罪 __ 提交于 2019-12-02 20:54:07

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.
bitbonk

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

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

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

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.

The most "XAML way" to achieve this goal I can think of is to create dedicated MarkupExtension which would pull the animation from the resources dictionary and set necessary properties - I assume those are limited to a subset of Storyboard.Target, Storyboard.TargetName and Storyboard.TargetProperty. Although it requires some code-behind, it is one-time effort, moreover, MarkupExtensions are designed to be used with XAML. Here's the simplest version:

[MarkupExtensionReturnType(typeof(Timeline))]
public class AnimationResourceExtension : StaticResourceExtension
{
    //This property is for convienience so that we
    //don't have to remember to set x:Shared="False"
    //on all animation resources, but have an option
    //to avoid redundant cloning if it is
    public bool IsShared { get; set; } = true;

    public DependencyObject Target { get; set; }

    public string TargetName { get; set; }

    public PropertyPath TargetProperty { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (base.ProvideValue(serviceProvider) is Timeline animation)
        {
            //If the animation is shared we shall clone it
            //Checking if it is frozen is optional and we can
            //either clone it or throw an exception
            //(or simply proceed knowing one will be thrown anyway)
            if (IsShared || animation.IsFrozen)
                animation = animation.Clone();
            Storyboard.SetTarget(animation, Target);
            Storyboard.SetTargetName(animation, TargetName);
            Storyboard.SetTargetProperty(animation, TargetProperty);
            return animation;
        }
        else
            throw new XamlException("The referenced resource is not an animation");
    }
}

Usage is very straightforward:

<FrameworkElement.Resources>
    <DoubleAnimation x:Key="MyAnimation" From="0" To="1" Duration="0:0:1" />
</FrameworkElement.Resources>
(...)
<Storyboard>
    <utils:AnimationResource ResourceKey="MyAnimation" TargetName="SomeElement" TargetProperty="Opacity" />
</Storyboard>

Being as simple as it can be this solution has its limitations - it does not support neither Binding nor DynamicResource extensions for aforementioned properties. This however is achievable, but requires some extra effort. Binding support should be pretty simple - a question of proper use of XamlSetMarkupExtensionAttribute (plus some boilerplate code). DynamicResource support would be a little trickier, and in addition to using XamlSetMarkupExtensionAttribute would require wrapping the IServiceProvider to return adequate IProvideValueTarget implementation, but is still possible.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!