2016-04-03 2 views
0

Ich habe eine Navigationsleiste, die einige Tasten enthält. Wenn der Benutzer auf einen von ihnen klickt, wird das gleiche Storyboard ausgelöst, aber ein anderer Event-Handler wird aufgerufen. Hier ist der Code für jede Taste:WPF: Wie effizient ein Storyboard für mehrere Steuerelemente mit der gleichen Animation und verschiedenen Event-Handler für "abgeschlossen" schreiben?

<Button x:Name="<!-- Button's name -->"> 
    <Button.Triggers> 
     <EventTrigger RoutedEvent="Button.PreviewMouseDown"> 
      <BeginStoryboard> 
       <Storyboard> 
        <DoubleAnimation 
        Storyboard.TargetName="FrameTest" 
        Storyboard.TargetProperty="Opacity" 
        To="0" 
        Duration="0:0:1" 
        Completed="<!-- Different event handler -->" /> 
       </Storyboard> 
      </BeginStoryboard> 
     </EventTrigger> 
    </Button.Triggers> 
    Messages 
</Button> 

Es gibt viele Tasten und das ist SEHR repetitiv. Gibt es eine Möglichkeit, es eleganter zu schreiben?

+1

Oh das ist einfach; Schieben Sie das fragliche Storyboard in ein Ressourcenwörterbuch, geben Sie ihm ein x: Key = "NameGoesHere" und verwenden Sie es dann immer wieder als Ressource. – Logan

Antwort

1

Ohne eine gute Minimal, Complete, and Verifiable example, die deutlich zeigt, was Sie erreichen wollen, ist es unmöglich zu wissen, was am besten für Sie arbeiten würde. Aber wenn ich Ihre Frage richtig verstehe, möchten Sie in der Lage sein, eine einzelne Deklaration eines Storyboard und/oder eines oder mehrerer Animation Objekte zu erstellen, dieses Objekt auf mehreren Button Objekten wiederzuverwenden, aber dennoch unterschiedliche Ereignisbehandlungen für die Objekte zu haben Completed Ereignis.

Leider ist das Completed Ereignis nicht weitergeleitet, so weit ich weiß, müssen Sie direkt abonnieren. Aber das lässt immer noch Raum für mindestens ein paar verschiedene Ansätze, von denen ich denke, dass sie funktionieren sollten.

IMHO, die einfachste ist eigentlich nur einen Ereignishandler zu haben, aber es in der Lage sein, die Vervollständigung für welches Ziel verwendet wurde. Beachten Sie, dass der Absender des Ereignisses Completed in dem von Ihnen bereitgestellten Beispiel die AnimationClock für die Zeitleiste ist. In Code-Behind können Sie die Zeitleiste (z. B. Ihr DoubleAnimation Objekt) von diesem abrufen und können dann die Zielinformationen aus der Animation über die statischen Methoden Storyboard abrufen. Zum Beispiel:

string targetName = Storyboard.GetTargetName(((AnimationClock)sender).Timeline); 

In Ihrem Beispiel oben würde die targetName Variable den Wert "FrameTest" erhalten. Auf diese Weise kann ein einzelner Event-Handler entsprechend antworten, welches Objekt gerade animiert wurde.

Beachten Sie, dass, wenn Sie die Storyboard Objekt als Ressource erklären gehen und dann auf mehrere Elemente wiederverwenden, werden Sie x:Shared="false" in seiner Erklärung enthalten sein sollen, um sicherzustellen, dass jedes Element, das es seine eigene Kopie wird verwendet von das Objekt Storyboard. Eine alternative Technik, die häufig in WPF verwendet wird und oft nützlich ist, um die Art und Weise zu erweitern, wie Objekte agieren, sind angefügte Eigenschaften und das zugehörige Konzept von "Verhaltensweisen" (etwas, das von den Expression-Erweiterungen unterstützt wird, das Sie aber auch selbst implementieren können) wenn du möchtest). Diese ermöglichen Ihnen, wie mit der angefügten Eigenschaft Storyboard.TargetName benutzerdefinierte Informationen und Aktionen für WPF-Objekte zu verwalten.

Sie könnten zum Beispiel erstellen eine angeschlossene Eigenschaft, die Sie ein ICommand Objekt auszuführen, einen Action Delegierten aufzurufen oder ein Routingereignis zu erhöhen, wenn die Completed-Ereignis ausgelöst definieren. Ihre angefügte Eigenschaft würde die entsprechende Konfiguration abhängig von Ihrer Implementierung durchführen, wenn die Eigenschaft festgelegt wird. Der Vorteil von so etwas ist, dass Sie das XAML verwenden könnten, um das Verhalten für jedes Zielelement (d. H. Die Button-Objekte) anzupassen, anstatt Informationen im gemeinsam genutzten Ereignishandler hart zu codieren.


Nachtrag:

Basierend auf Ihre Kommentare und Verweis auf die andere Frage, die Sie geschrieben, es scheint, dass Sie die folgenden zusätzlichen Kriterien haben:

  • Es gibt ein einzelnes Objekt animiert werden , während Sie mehrere Schaltflächen haben, die alle dieses Objekt betreffen.
  • Das Objekt ist ein Objekt Frame, und Sie möchten die Content Eigenschaft dieses Objekts aktualisieren können, wenn die Animation abgeschlossen ist.
  • Sie möchten das Objekt Storyboard einmal deklarieren und in der Lage sein, die Animation durch jede Schaltfläche initiieren zu lassen und dann individuell festzulegen, was nach Abschluss der Animation geschehen soll.

    Dieser dritte Punkt ist problematisch, weil die Storyboard und ihre Animationen nicht einmal das Button Objekt kennen, das sie initiiert, und so die Completed Event-Handler der Animation das Verhalten basierend auf der Button Instanz, die gestartet wurde, ist nicht ohne zusätzliche möglich Anstrengung.

Basierend auf diesem Verständnis unten ist ein einfaches Codebeispiel, das eine mögliche Art und Weise zeigt die oben erreicht werden kann.

(Ich betone "einen möglichen Weg", weil eine der Konsequenzen der großen Flexibilität von WPF ist, dass oft eine verblüffend große Anzahl verschiedener Möglichkeiten besteht, dasselbe Ziel zu erreichen. Diejenigen von uns, die keine Experten sind in WPF dann feststellen, dass wir etwas zu tun, möglicherweise nicht die beste Art und Weise kennen oder auch nicht, weil wir einig Weg gefunden, es zu tun und hielten dort :))

Sowieso & hellip.

Zunächst stützt sich das gesamte Beispiel auf einer benutzerdefinierten angefügten Eigenschaft, hier gezeigt (dies das „Verhalten“ Idiom zu — eine besonderen Art der angefügten Eigenschaft getan werden kann, mit — aber IMHO das würde das Beispiel nur unnötig komplizieren, auch wenn es ist ein schöner Weg, es zu tun):

class AttachStoryboard 
{ 
    public RoutedEvent Trigger { get; set; } 
    public Storyboard Storyboard { get; set; } 
    public event EventHandler Completed; 

    public void RaiseCompleted() 
    { 
     EventHandler handler = Completed; 

     if (handler != null) 
     { 
      handler(this, EventArgs.Empty); 
     } 
    } 
} 

static class StoryboardHelper 
{ 
    public static readonly DependencyProperty AttachStoryboardProperty = DependencyProperty.RegisterAttached(
     "AttachStoryboard", typeof(AttachStoryboard), typeof(StoryboardHelper), new PropertyMetadata(_OnAttachStoryboardChanged)); 

    private static void _OnAttachStoryboardChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     FrameworkElement target = (FrameworkElement)d; 
     AttachStoryboard attachStoryboard = (AttachStoryboard)e.NewValue; 

     if (attachStoryboard != null) 
     { 
      BeginStoryboard beginStoryboard = new BeginStoryboard { Storyboard = attachStoryboard.Storyboard }; 
      EventTrigger trigger = new EventTrigger(attachStoryboard.Trigger); 

      trigger.Actions.Add(beginStoryboard); 
      attachStoryboard.Storyboard.Completed += (sender, e1) => attachStoryboard.RaiseCompleted(); 

      target.Triggers.Add(trigger); 
     } 
    } 

    public static void SetAttachStoryboard(FrameworkElement target, AttachStoryboard value) 
    { 
     target.SetValue(AttachStoryboardProperty, value); 
    } 

    public static AttachStoryboard GetAttachStoryboard(FrameworkElement target) 
    { 
     return (AttachStoryboard)target.GetValue(AttachStoryboardProperty); 
    } 
} 

Als nächstes benötigen wir einige Event-Handler in der Code-Behind zu erklären. In diesem Fall eine pro Schaltfläche, aber natürlich könnten Sie dies weiter verallgemeinern, indem Sie der angefügten Eigenschaft mehr Kontext hinzufügen, die es einem Ereignishandler ermöglichen würde, die Quelle zu kennen.

Hinweis: weil es nicht der Kontext ein einzelner Event-Handler zu ermöglichen, um die Animation Abschluss für jede Taste zu handhaben, ist es wichtig, dass jede Taste eine eigene private Kopie der Storyboard erhält, von x:Shared="false" (siehe XAML unten). Eine Motivation für die weitere Verallgemeinerung wäre also, dass Sie mit einem einzigen Storyboard Ressourcenobjekt davonkommen und x:Shared="false" nicht setzen müssten.

Die gesamten MainWindow.cs des Beispiels:

public partial class MainWindow : Window 
{ 
    private readonly Storyboard storyboard2; 

    public MainWindow() 
    { 
     InitializeComponent(); 

     storyboard2 = (Storyboard)FindResource("storyboard2"); 
    } 

    private void button1_Completed(object sender, EventArgs e) 
    { 
     frame1.Content = "Button #1 Content"; 
     storyboard2.Begin(frame1); 
    } 

    private void button2_Completed(object sender, EventArgs e) 
    { 
     frame1.Content = "Button #2 Content"; 
     storyboard2.Begin(frame1); 
    } 

    private void button3_Completed(object sender, EventArgs e) 
    { 
     frame1.Content = "Button #3 Content"; 
     storyboard2.Begin(frame1); 
    } 
} 

Für jede Taste, aktualisiert er die Content Eigenschaft des Frame Objekts, und dann beginnt die „Fade-in“ Animation den aktualisierten Inhalt zu offenbaren.

Schließlich gibt es die XAML:

<Window x:Class="TestSO36386403SharedStoryboard.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:s="clr-namespace:System;assembly=mscorlib" 
     xmlns:l="clr-namespace:TestSO36386403SharedStoryboard" 
     Title="MainWindow" Height="350" Width="525"> 
    <Window.Resources> 
    <Style TargetType="Button"> 
     <Setter Property="HorizontalAlignment" Value="Left"/> 
     <Setter Property="Margin" Value="5"/> 
    </Style> 
    <Storyboard x:Key="storyboard1" x:Shared="false"> 
     <DoubleAnimation Storyboard.TargetName="frame1" 
         Storyboard.TargetProperty="Opacity" 
         To="0" Duration="0:0:1" FillBehavior="Stop"/> 
    </Storyboard> 
    <Storyboard x:Key="storyboard2"> 
     <DoubleAnimation Storyboard.TargetName="frame1" 
         Storyboard.TargetProperty="Opacity" 
         To="1" Duration="0:0:1" FillBehavior="Stop"/> 
    </Storyboard> 
    </Window.Resources> 
    <StackPanel> 
    <Button x:Name="button1" Content="Button #1"> 
     <l:StoryboardHelper.AttachStoryboard> 
     <l:AttachStoryboard Trigger="Button.PreviewMouseDown" 
          Storyboard="{StaticResource storyboard1}" 
          Completed="button1_Completed"/> 
     </l:StoryboardHelper.AttachStoryboard> 
    </Button> 
    <Button x:Name="button2" Content="Button #2"> 
     <l:StoryboardHelper.AttachStoryboard> 
     <l:AttachStoryboard Trigger="Button.PreviewMouseDown" 
          Storyboard="{StaticResource storyboard1}" 
          Completed="button2_Completed"/> 
     </l:StoryboardHelper.AttachStoryboard> 
    </Button> 
    <Button x:Name="button3" Content="Button #3"> 
     <l:StoryboardHelper.AttachStoryboard> 
     <l:AttachStoryboard Trigger="Button.PreviewMouseDown" 
          Storyboard="{StaticResource storyboard1}" 
          Completed="button3_Completed"/> 
     </l:StoryboardHelper.AttachStoryboard> 
    </Button> 
    <Frame x:Name="frame1" Content="Initial Content"/> 
    </StackPanel> 
</Window> 

Die oben einfach erklärt die beiden Storyboard Ressourcen verwendet werden und initialisiert die angefügten Eigenschaft für jeden geeignet Taste.

+0

Vielen Dank für die ausführliche Antwort. Du verstehst mein Problem perfekt. Könnten Sie bitte demonstrieren, wie Ihre Vorschläge aussehen? In Ihrer ersten Lösung ist der "targetName" immer der Frame, aber ich muss wissen, welcher Button die Animation ausgelöst hat und nicht das Ziel dieser Animation. Was vermisse ich hier? Vielen Dank, Peter! – Sipo

+0

Ich habe auch eine weitere Frage mit einer vollständigeren und ausführlicheren Erklärung der allgemeinen Situation, in der ich mich befinde, gepostet. Es ist nicht genau die gleiche Frage, also habe ich eine neue gepostet, bitte seht es hier: http://stackoverflow.com/questions/36402710/wpf-how-to-create-a-Navigationssystem-with-a-shared-completed-event-for-the-p – Sipo

+0

Bitte beachten Sie meine Bearbeitung, mit einem Beispiel, wie die Verwendung einer angefügten Eigenschaft aussehen könnte. –

Verwandte Themen