Question

Y at-il un moyen de définir une animation quelque part dans XAML (par exemple. Comme une ressource) une fois, puis le réutiliser plusieurs fois? J'ai beaucoup de brosses indépendantes à travers DataTemplates differnt qui ont besoin indépendamment pour démarrer le même genre d'animation basée sur une DataTrigger. Maintenant, car il semble que l'animation doit définir un Storyboard.TargetName et Storyboard.TargetProperty. Ce à peu près défaites dans le but de réutilisabilité. Je tiens à déclarer en quelque sorte « utiliser cette animation forme la ressource, mais l'appliquer à un autre élément cette fois-ci ».

Pour moi, cela semble être une demande assez simple, important et essentiel et je suis surpris de voir que son pas tout droit à acomplish. Est-ce que je manque quelque chose ici?

La même chose vaut pour les déclencheurs. Supposons que j'ai beaucoup d'éléments visuels differnt que tous représentent le même type d'état en utilisant des animations de couleurs. Par exemple. fondu au vert lorsque « actif » fondu à « rouge » quand « erreur », etc. La seule différence entre les visuels est leur arbre forme / visuel le comportement d'animation souhaité est le même, ils ont tous quelque part un élément dans leur arbre visuel qui a une propriété de couleur de type. Je pense qu'il est pas difficile d'imaginer à quel point il est fastidieux de redéfinir les mêmes animations et DataTrigger jeux encore et encore. Chaque développeur déteste cela. Je cherche désespérément une solution plus facile qui ne nécessite pas non (ou très peu) c # code derrière.

Ce que je suis venu avec à ce jour est le suivant:

Définir les animations dans une ressource lik ce (Répétez cette opération pour tous les états de base qu'il ya, comme l'activation, actif, inactif, erreur):

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

L'utilisation en story-board dans les déclencheurs (répéter cette zillions de fois pour chaque état X chaque differnt stateviusal, viennent toujours avec un nouveau nom pour le story-board):

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

Vous pouvez facilement imaginer comment XAML beaucoup ballonnement je dois copier et coller à plusieurs reprises pour tous les DataTriggers zillion.

Il serait cool de définir tout cela se déclenche une fois et l'appliquer à différents visuels de l'État. Comment est quelque chose comme ça résolu dans WPF? Toute astuce?

Était-ce utile?

La solution 2

Il ne semble pas être une bonne solution XAML seule à ce problème général. Je fini par écrire mes propres propriétés attachées qui définissent tous les comportements d'animation pour un élément donné. Quelque chose comme ceci:

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

Le reste (la création de l'animation, etc.) se fait dans le behind.

Autres conseils

Pouvez-vous essayer quelque chose comme ça?

  • Enveloppez tous vos modèles de contrôle actuels avec un élément racine invisible, par exemple une bordure ou un StackPanel, dont la boîte de délimitation couvre la totalité du contrôle.
  • Créer un style ou modèle de contrôle pour cette boîte invisible qui contient tous les éléments déclencheurs et des animations.
  • Avoir les animations animent une propriété de couleur arbitraire sur la boîte invisible.
  • Dans les arbres visuels pour tous vos différents contrôles, lier les propriétés que vous souhaitez animer à la propriété couleur sur l'élément racine invisible.

Je me rends compte de cette question est un peu mort au moment de cette annonce, mais j'ai trouvé une solution qui ne nécessite que très peu de code derrière.

Vous pouvez faire un UserControl avec des propriétés personnalisées (Faites défiler la liste à environ la figure 8) qui contient votre rectangle, ainsi que les animations et les déclencheurs de l'État. Ce contrôle utilisateur définirait une propriété publique comme le statut qui déclencherait le changement de couleur lorsqu'il est modifié.

Le code-behind nécessaire uniquement est de créer vos variables dans le code.

Le contrôle utilisateur peut les être réutilisés encore et encore sans réécrire le storyboards XAML ou déclenche des données.

Le plus « XAML chemin » pour atteindre cet objectif que je peux penser est de créer MarkupExtension dédié qui rassemblerait l'animation du dictionnaire des ressources et définir les propriétés nécessaires - Je suppose que ceux-ci sont limités à un sous-ensemble de Storyboard.Target, Storyboard.TargetName et Storyboard.TargetProperty. Bien qu'il nécessite un code-behind, il est effort ponctuel, par ailleurs, MarkupExtensions sont conçus pour être utilisés avec XAML . Voici la version la plus simple:

[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");
    }
}

L'utilisation est très simple:

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

Être aussi simple que possible cette solution a ses limites - il ne supporte pas ni extensions Binding ni DynamicResource pour les propriétés mentionnées ci-dessus. Cela est toutefois réalisable, mais nécessite un effort supplémentaire. soutien Binding devrait être assez simple - une question de bonne utilisation de XamlSetMarkupExtensionAttribute  (Plus un code de passe-partout). soutien DynamicResource serait un peu plus délicat, et en plus d'utiliser XamlSetMarkupExtensionAttribute nécessiterait l'emballage IServiceProvider pour revenir adéquat la mise en œuvre de IProvideValueTarget, mais il est encore possible.

scroll top