Domanda

C'è un modo per definire un'animazione da qualche parte in XAML (ad es. Come risorsa) una volta e poi riutilizzarla più volte? Ho un sacco di pennelli indipendenti in tutta DataTemplates differnt che hanno bisogno in modo indipendente per avviare lo stesso tipo di animazione basata su un DataTrigger. Ora, poiché sembra che l'animazione deve definire uno Storyboard.TargetName e Storyboard.TargetProperty. Questo più o meno sconfigge lo scopo di riutilizzabilità. Vorrei un po 'come a dichiarare "utilizzare questa animazione costituiscono la risorsa, ma applicarlo ad un altro elemento di questa volta".

A me questo sembra essere una richiesta piuttosto di base, importante ed essenziale e mi sono sorpreso che la sua non è che semplice da compire. Mi sto perdendo qualcosa qui?

La stessa cosa vale per i trigger. Supponiamo che io sono un sacco di elementi visivi differnt che tutti rappresentano lo stesso tipo di stato utilizzando animazioni di colore. Per esempio. dissolvenza verde quando dissolvenza "attivo" a "rosso" quando "errore" ecc L'unica differenza tra le immagini è la loro forma / albero visuale il comportamento animazione desiderata è la stessa, hanno tutti un elemento in qualche parte l'albero visivo contenente una proprietà di tipo di colore. Penso che non è difficile immaginare come noioso è quello di ridefinire le stesse animazioni e DataTrigger set più e più volte. Ogni sviluppatore odia questo. Ho disperatamente cercano una soluzione più semplice che non richiede nessuna (o almeno molto poco) codice C # dietro.

Quello che ho elaborato finora è questa:

Definisci le animazioni in una risorsa lik questo (Ripetete l'operazione per tutti gli stati di base che ci sono, come l'attivazione, attivo, inattivo, errore):

<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'uso in storyboard nei trigger (ripetere questo un'infinità di volte per ogni Stato X ogni stateviusal differente, sempre venire con un nuovo nome per lo 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>

Si può facilmente immaginare quanto XAML gonfiare devo copiare e incollare più volte per tutte quelle DataTriggers zillion.

Sarebbe bello per definire tutto questo si innesca una volta e applicarla a differenti visuali di stato. Come è qualcosa di simile risolto in WPF? Qualsiasi suggerimento?

È stato utile?

Soluzione 2

Non sembra esserci alcuna buona soluzione XAML-solo a questo problema generale. Ho finito per scrivere le mie proprietà associate che definiscono tutti i comportamenti di animazione per un dato elemento. Qualcosa di simile a questo:

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

Il resto (creando l'animazione, ecc) è fatto in codebehind.

Altri suggerimenti

Potresti provare qualcosa di simile?

  • Avvolgere tutti i modelli di controllo correnti con un elemento radice invisibile, per esempio un bordo o uno StackPanel, il cui riquadro di delimitazione coprirà l'intero controllo.
  • Creare uno stile o un modello di controllo per questa scatola invisibile che contiene tutti i trigger e le animazioni.
  • Avere le animazioni animare una proprietà colore arbitrario sulla scatola invisibile.
  • Negli alberi visivi per tutti i diversi controlli, associare le proprietà che si desidera animare alla proprietà colore sull'elemento radice invisibile.

Mi rendo conto che questo problema è un po 'morto al momento di questo intervento, ma ho trovato una soluzione che richiede pochissimo codice dietro.

È possibile effettuare una UserControl con proprietà personalizzate (Scorrere verso il basso a circa figura 8) che contiene il rettangolo, così come le animazioni e trigger statali. Questo controllo utente potrebbe definire una proprietà pubblica, come lo stato che farebbe scattare il cambiamento di colore quando alterata.

L'unica code-behind necessaria è quella di creare le variabili nel codice.

Il controllo utente li può essere riutilizzato più e più volte senza dover riscrivere lo storyboard XAML o dati attiva.

Il più " XAML modo" per raggiungere questo obiettivo mi viene in mente è quello di creare MarkupExtension dedicato che tirava l'animazione dal dizionario risorse e impostare le proprietà necessarie - presumo quelli sono limitati ad un sottoinsieme di Storyboard.Target, Storyboard.TargetName e Storyboard.TargetProperty. Anche se richiede un codice-dietro, è lo sforzo di una volta, inoltre, MarkupExtensions sono progettati per essere utilizzati con XAML . Ecco la versione più semplice:

[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'uso è molto semplice:

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

Essendo così semplice come può essere questa soluzione ha i suoi limiti - non supporta né estensioni BindingDynamicResource per le proprietà di cui sopra. Questo però è realizzabile, ma richiede qualche sforzo in più. Supporto Binding dovrebbe essere abbastanza semplice - una questione di uso corretto di XamlSetMarkupExtensionAttribute  (Più alcuni codice standard). Supporto DynamicResource sarebbe un po 'più complicato, e oltre a utilizzare XamlSetMarkupExtensionAttribute richiederebbe avvolgendo il IServiceProvider per tornare adeguata implementazione IProvideValueTarget , ma è ancora possibile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top