2009-10-31 7 views
13

Gibt es eine Möglichkeit, eine Animation irgendwo in XAML (zB als Ressource) einmal zu definieren und sie dann mehrfach zu verwenden? Ich habe viele unabhängige Pinsel über verschiedene Datatemplates, die unabhängig voneinander die gleiche Art von Animation basierend auf einem Datentrigger starten müssen. Jetzt scheint es so zu sein, dass eine Animation ein Storyboard.TargetName und Storyboard.TargetProperty definieren muss. Dies widerlegt ziemlich genau den Zweck der Wiederverwendbarkeit. Ich würde irgendwie gerne erklären "benutze diese Animation als Ressource, aber wende es diesmal auf ein anderes Element an".Definieren Sie Animationen und Trigger als wiederverwendbare Ressource?

Für mich scheint dies eine ziemlich grundlegende, wichtige und wesentliche Anfrage zu sein, und ich bin überrascht, dass es nicht so direkt zu acomplish ist. Fehle ich hier etwas?

Das gleiche gilt für Trigger. Angenommen, ich habe viele verschiedene visuelle Elemente, die alle die gleiche Art von Zustand mit Farbanimationen darstellen. Z.B. Fade zu Grün, wenn "aktiv" zu "rot" verblasst, wenn "Fehler" usw. Der einzige Unterschied zwischen den Visuals ist ihre Form/visueller Baum das gewünschte Animationsverhalten ist das gleiche, sie alle haben ein Element irgendwo in ihrem visuellen Baum, der hat eine Eigenschaft vom Typ Farbe. Ich denke, es ist nicht schwer sich vorzustellen, wie mühsam es ist, die gleichen Animationen und Datentriggersätze immer wieder neu zu definieren. Jeder Entwickler hasst dies. Ich suche verzweifelt nach einer einfacheren Lösung, die keinen (oder zumindest sehr wenig) C# Code benötigt.

Was ich mit so weit gekommen sind, ist dies:

Definieren Sie die Animationen in einer Ressource lik diese (dies wiederholen für alle Grundzustände, dass es, wie die Aktivierung, aktiv, inaktiv, Fehler):

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

die Verwendung es in Storyboard in den Auslösern (wiederholen sie diese zig Millionen mal für jeden Zustand X jeden differnt stateviusal, immer mit einem neuen Namen für das Storyboard kommen):

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

Sie können sich leicht vorstellen, wie viel Bloat XAML ich für all diese zig DataTriggers kopieren und einfügen muss.

Es wäre cool, alle diese Trigger einmal zu definieren und sie auf verschiedene Zustandsbilder anzuwenden. Wie ist so etwas in WPF gelöst? Irgendwelche Trinkgeld?

Antwort

1

Es scheint keine gute XAML-Lösung für dieses allgemeine Problem zu geben Ich habe meine eigenen angehängten Eigenschaften geschrieben, die das gesamte Animationsverhalten für ein bestimmtes Element definieren. etwas wie das:

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

Der Rest (Erstellen der Animation usw.) ist im Codebehind erfolgt.

+0

Ich weiß, ich habe eine Menge verschiedener Dinge ausprobiert, um einen Vorschlag dafür zu finden, und es war einfach nicht möglich, ohne etwas im Code zu tun. Das ist so eine gute Lösung, wie ich denke, du wirst es bekommen. Hurra für WPF Erweiterbarkeit, oder? :) –

+0

Es könnte eine gute Idee sein, die silverlight Verhaltens-API dafür zu verwenden. Sind Sie einverstanden? Ist es kompatibel mit dem Standard-.NET-Framework? – bitbonk

+0

Nein, es ist nicht kompatibel, obwohl sie angenommen werden. Ehrlich gesagt, denke ich, dass Sie eine großartige Lösung gefunden haben, die die WPF-Architektur auf eine sehr "natürliche" Art und Weise nutzt und sich hier nicht selbst raten sollte. –

3

Können Sie so etwas versuchen?

  • Umschließen Sie alle Ihre aktuellen Steuerelementvorlagen mit einem unsichtbaren Stammelement, z. ein Border oder ein StackPanel, dessen Begrenzungsbox das gesamte Steuerelement abdeckt.
  • Erstellen Sie eine Stil- oder Steuerelementvorlage für diese unsichtbare Box, die alle Ihre Auslöser und Animationen enthält.
  • Lassen Sie die Animationen eine beliebige Color-Eigenschaft auf der unsichtbaren Box animieren.
  • In den visuellen Bäumen für alle Ihre verschiedenen Steuerelemente binden Sie alle Eigenschaften, die Sie animieren möchten, an die Color-Eigenschaft auf dem unsichtbaren Stammelement.
  • 0

    Ich weiß, dass dieses Problem zum Zeitpunkt dieses Postings ein wenig tot ist, aber ich habe eine Lösung gefunden, die sehr wenig Code benötigt.

    Sie können eine UserControl with custom properties erstellen (Scrollen Sie nach unten zu Abbildung 8), die Ihr Rechteck sowie die Animationen und Statusauslöser enthält. Dieses Benutzersteuerelement würde eine öffentliche Eigenschaft definieren, z. B. den Status, der bei Änderung die Farbänderung auslösen würde.

    Das einzige Code-Behind benötigt, um Ihre Variablen im Code zu erstellen.

    Das Benutzersteuerelement kann wiederverwendet werden, ohne die XAML-Storyboards oder Datenauslöser neu schreiben zu müssen.

    0

    Die „XAML Art und Weise“ Um dieses Ziel zu erreichen, ich ist denken kann gewidmet MarkupExtension zu schaffen, die die Animation aus dem Ressource-Wörterbuch und stellten notwendige Eigenschaften ziehen würde - ich diejenigen, auf eine Teilmenge von Storyboard.Target begrenzt sind annehmen, Storyboard.TargetName und Storyboard.TargetProperty. Obwohl es einige Code-Behind erfordert, ist es einmalige Bemühung, außerdem MarkupExtension s sind entworfen, um mit XAML verwendet zu werden. Hier ist die einfachste 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"); 
        } 
    } 
    

    Die Nutzung ist sehr einfach:

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

    Sein so einfach, wie es diese Lösung hat seine Grenzen sein kann - es ist nicht weder Binding noch DynamicResource Erweiterungen für oben genannten Eigenschaften nicht unterstützt. Dies ist jedoch erreichbar, erfordert jedoch zusätzlichen Aufwand. Binding Unterstützung sollte ziemlich einfach sein - eine Frage der ordnungsgemäßen Verwendung von XamlSetMarkupExtensionAttribute (plus einige Boilerplate-Code). DynamicResource Unterstützung wäre ein wenig komplizierter, und zusätzlich zur Verwendung von XamlSetMarkupExtensionAttribute würde erfordern, dass die IServiceProvider um IProvideValueTarget Implementierung zurückgeben, aber noch möglich ist.

    Verwandte Themen