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