Das folgende Beispiel zeigt, wohin die Reise geht:
Grundlegende Funktionsweise
Um ein ChildWindow zu Rotieren, muss in das Template eingegriffen werden. Ohne das bekommt man ein ChildWindow nicht animiert. Dem Template des ChildWindow werden einige Storyboard hinzugefügt, die für die Rotation verantwortlich sind. Das ContentRoot, Teil des ChildWindow Templates, wird im Verlauf eines Storyboards über die x-, y- bzw. sowhl über die x- als auch über die y-Achse um jeweils 360° gedreht. Für die Drehungen benutzen wir jeweils eine bzw. mehrere PlaneProjektion. Für das angezeigte ChildWindow werden die Rotationen, bzw. die Storyboards, zu Demonstrationszwecken jeweils durch Click auf den entsprechenden Button ausgelöst. Dabei nutzen wir die Methode FindName() des Root Elements, das seinerseits Teil (Template Part) des ChildWindow ist, und rufen einfach das entsprechende Storyboard auf.
Um eine Rotationsanimation unmittelbar beim Laden eines ChildWindows aufzurufen, müssen wir eine kleine trickreiche Vorbereitung treffen, damit sichergestellt ist, dass die Animation gena dann ausgeführt wird, wenn das ChildWindow vollständig geladen ist. Ansonsten ist die Funktionsweise identisch. Also, auf geht's - Schritt für Schritt.
Schritt für Schritt
Schritt 1
In einem neuen Silverlight 4 Projekt fügen wir über einen Rechtsklick auf das Projekt ein neues Element hinzu ...
... und benennen es mit RotatingChildWindow.xaml.
Es erscheint das soeben erzeugte ChildWindow und wir selektieren in der Registerkarte Objekte und Zeitachsen das oberste Element: ChildWindow. Rechtsklick, Vorlage bearbeiten ..., Kopie bearbeiten ...
Dadurch fügen wir einen neuen Style hinzu, den wir RotatingChildWindowStyle nennen. Klick auf Ok ...
... und in der Registerkarte Objekte und Zeitachsen landen wir sofort im visuellen Baum (VisualTree) der Vorlage unseres ChildWindow.
Mit F6 rufen wir den Animationsarbeitsbereich auf und fügen dort ein neues Storyboard hinzu. Das nennen wir sbRotateX:
Klick ok. Dann selektieren wir im visuellen Baum unserer ChildWindow Vorlage das ContentRoot Element und fügen dem Storyboard sbRotateX einen ersten Keyframe bei der Zeitachsen-Position 0:00,000 hinzu:
Wir fügen einen zweiten Keyframe bei der Zeitachsen-Position 0:01,000, also bei 1 Sekunde, hinzu.
Das ContentRoot ist weiterhin selektiert, und wir fügen dem ContentRoot für diesen Keyframe eine PlaneProjection von 360° über die X-Achse hinzu:
Damit ist das erste Storyboard für die Rotation über die X-Achse fertig. Für die Rotationen über die Y- bzw. die X- und Y-Achsen gehen wir gleichermaßen vor, nur dass für die Y-Achsen-Rotation eine PlaneProjection von 360° über die Y-Achse und für die X- und Y-Achsen-Rotation eine PlaneProjection von 360° über die X- und eine die Y-Achse beim Keyframe 0:01,000 gesetzt werden. Die beiden weiteren Storyboards nenn wir sbRotateY bzw. sbRotateXY.
Wir schließen das letzte Storyboard und wechseln mit F6 wieder in die Entwurfsansicht.
Schirtt 2
Wir wechseln dann zurück in die ChildWindow-Ansicht, also hinaus aus der Vorlage, indem wir auf den kleinen oberen Pfeil-Button in der Registerkarte Objekte und Zeitachsen klicken.
Wir aktivieren MainPage.xaml und fügen dem LayoutRoot einen Button hinzu, btShow, der unser ChildWindow aufrufen soll.
Schritt 3
Jetzt wechseln wir rüber zu Visual Studio 2010, um den CodeBehind hinzuzufügen. Als erstes fügen wir in MainPage.xaml.vb dem Click EventHandler des btShow den Code für den Aufruf des ChildWindow hinzu.
Dann öffnen wir RotatingChildWindow.xaml.vb und fügen als Erstes den folgenden Code-Ausschnitt hinzu:
Dieser Code garantiert uns letztlich den Zugriff auf das Root Element und stellt damit sicher, dass wir Zugriff auf die erzeugten Storyboards bekommen. Denn wir mußten die Storyboards ja in der Vorlage des Storyboards erzeugen und um sie zu starten benötigen wir zur Laufzeit wiederum Zugriff auf diese Storyboards in der Vorlage. Der Zugriff auf die Storyboards funktioniert im Ergebnis über die Methode FindName() des Root Elements. Um diese Methode aufzurufen, benötigen wir natürlich das Root Element, das Bestandteil des visuellen Baums (VisualTree) der Vorlage (Template) des ChildWindow ist. Beim Aufrufen eines ChildWindow ist es jedoch so, dass der VisualTree des ChildWindow nicht immer vollständig geladen ist, wenn das Loaded-Ereignis des ChildWinodw durchgelaufen ist. Klingt paradox, denn man möchte ja meinen, dass Loaded (zu Deutsch: geladen) signalisiert, dass das ChildWindow tatsächlich und vor allem vollständig geladen ist. Genau das ist aber nicht garantiert. Probiert es selbst mal aus. Mal ist der VisualTree vollständig geladen, und mal nicht. Deswegen benötigen wir einen kleinen Trick, der genau das sicherstellt. Und der funktioniert so:
Der Code fügt eine private Variable root vom Typ FrameworkElement hinzu, ferner den Delegaten ThisChildWindow_LoadedDelegate. Im Loaded-Ereignis des ChildWindow prüfen wir den VisualTree der Vorlage und schauen in der If-Verzweigung mittels VisualTreeHelper.GetChildrenCount nach, wieviele Kind-Elemente des VisualTree bereits geladen sind. Das machen wir, um der privaten Variablen root das Root Element des VisualTree zuweisen zu können. Das Root Element ist das oberste bzw. erste Element im VisualTree der Vorlage des ChildWindow. Die If-Verzweigung prüft nun, ob die Anzahl der geladenen Kind-Elemente der ChildWindow-Vorlage gleich 0 ist. Und solange das der Fall ist, wird über die Methode Dispatcher.BeginInvoke(...) das Loaded-Ereignis erneut aufgerufen, indem der übergebene Delegat ThisChildWindow_LoadedDelegate, der auf den Loaded-EreignisHandler zeigt, erneut aufgerufen wird. Die If-Verzweigung gibt False zurück, sobald die Anzahl der Kind-Elemente > 0 ist, also mindestens ein Element, nämlich unser gesuchtes Root-Element, geladen ist. Sobald False zurückgegeben wird, kann in der Else-Verzweigung der privaten Variablen root das Root-Element des VisualTree zugewiesen werden.
Ziemlich tricky, aber es funktioniert. (Übrigens gleichermaßen für MainPage_Loaded).
Schritt 4
Jetzt sind die Vorbereitungen getroffen, um auf die in der Vorlage definierten Storyboards zuzugreifen und sie zu starten. Um auf die Storyboards sbRotateX, sbRotateY bzw. sbRotateXY zuzugreifen, benutzen wir die Methode FindName() unserer privaten Variable root, der wir das Root-Element zugewiesen haben. Hier ist der Code:
Die Routine StartRotating nimmt den Namen eines Storyboard auf, erzeugt das Storyboard sb und weist diesem über root.FindName(StoryboardName) das gewünschte Storyboard zu. sb.Begin() ruft schießlich das Storyboard auf.
Das war's.
Schließlich: Aufruf des ChildWindow mit der Rotationsanimation
Um eine Rotationsanimation direkt beim Aufruf des ChildWindow ablaufen zu lassen, brauchen wir nur im Loaded-EreignisHandler die Routine StartRotating aufzurufen. Der Code für den Loaded-EreignisHandler sieht dann im Ergebnis so aus.
Mit der gleichen Technik kann man einem ChildWindow auch andere Animation hinzufügen.
Beispiel-Quellcode findet Ihr in der Expression Gallery als Download. Der Download enthält ein Silverlight 3-Projekt, das aber problemlos in Silverlight 4 konvertiert werden kann und dann einwandfrei funktioniert.
Der Style für das ChildWindow, das ich im Beispiel verwendet habe, das Ihr am Anfang dieses Artikels findet, findet Ihr nicht im Download der Expression Gallery. Dafür gibt es hier den entsprechenden XAML-Code:
Viel Spaß also mit rotierenden ChildWindows.
Keine Kommentare:
Kommentar veröffentlichen