Po długiej przerwie związanej z przeprowadzką, czas powrócić do prowadzenia blog’a i pisania artykułów. Mam nadzieję, że od dziś posty będą pojawiać się częściej i bardziej regularnie chociaż nie wszystko jeszcze idzie po mojej myśli.
No to zaczynamy… Visual State Manager został wprowadzony w wersji 4.0 Wcześniej mogliśmy go spotkać w Silverlight lub WPF Toolkit. Służy do zmieniania wyglądu kontrolek na podstawie zdefiniowanych stanów. Zastosowanie jest podobne więc do triggerów jednak różnica polega na tym, że wyzwalacze odpalają się po zmianie właściwości kontrolki (lub na skutek zdarzenia), z kolei StateManager zawiera zdefiniowane stany, które programista może ręcznie uruchomić
Najpierw definiujemy grupy stanów (VisualStateGroup), potem konkretne stany a w nich StoryBoard określający zmianę wyglądu. Ponadto można również zdefiniować przejście z jednego stanu do drugiego ( opóźnienie).
W Internecie widziałem wiele przykładów pokazujących jak działa VisualStateManager. Szczerze mówiąc miałem problemy z ich zrozumieniem – moim zdaniem nie pokazywały one istoty mechanizmu . Z tego względu na blogu przedstawię maksymalnie uproszczony przykład zastosowania (typowo akademicki).
Zacznijmy od zdefiniowania pojedynczej grupy i stanu (przejścia ominiemy):
<VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="StateA"> <Storyboard> <DoubleAnimation Storyboard.TargetName="button1" Storyboard.TargetProperty="Width" To="50" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
Najważniejsze w powyższym kodzie to zdefiniowanie stanu StateA, który zmienia szerokość kontrolki o nazwie button1. Definicja stanów zawsze przybiera postać animacji (storyboard). W przykładzie zostanie uruchomiona animacja zmniejszająca (lub zwiększająca) szerokość do 50. Możemy oczywiście definiować różne stany i w ten sposób programować proste workflow’y. Zmiana interfejsu użytkownika może zostać w bardzo prosty sposób zdefiniowana przez stany a następnie z poziomu kodu programista może sterować wyglądem interfejsu. W c# aby przejść do wskazanego stanu wystarczy:
VisualStateManager.GoToElementState((FrameworkElement)LayoutRoot, "StateA", true);
Pierwszy parametr określa kontrolkę w której zostały zdefiniowane stany, drugi to oczywiście nazwa konkretnego stanu a ostatni definiuje czy należy wykonać przejścia między stanami (w tym przypadku nie zdefiniowano żadnych). Na zakończenie tego przykładu warto wskazać miejsce w którym powinny zostać zdefiniowane stany. Jak widać z wywołania GoToElementState stany zostały zdefiniowane w kontrolce o nazwie LayoutRoot (Grid). Dla jasności wklejam całość kodu:
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="VisualStateManager" Height="191" Width="337"> <Grid x:Name="LayoutRoot"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="StateA"> <Storyboard> <DoubleAnimation Storyboard.TargetName="button1" Storyboard.TargetProperty="Width" To="50" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Button Width="100" Height="100" x:Name="button1" Click="button1_Click" Margin="14,6,39,12"/> </Grid> </Window>
Możemy zdefiniować również czas trwania przejścia. Przykład A:
<VisualStateGroup x:Name="CommonStates" > ... <VisualStateGroup.Transitions> <VisualTransition GeneratedDuration="0:0:5"/> </VisualStateGroup.Transitions> </VisualStateGroup>
Powyższy kod spowoduje, że przejście do każdego stanu będzie trwało 5 sekund. Ponadto możemy określić czas wymagany do przejścia ze stanu A do stanuB:
<VisualStateGroup x:Name="CommonStates" > ... <VisualStateGroup.Transitions> <VisualTransition From="StateA" To="StateB" GeneratedDuration="0:1:0.1"/> </VisualStateGroup.Transitions> </VisualStateGroup>
Na dobry początek chyba wystarczy. Nie jest to skomplikowany mechanizm ale chyba wciąż mało popularny. W WPF toolkit istnieje od dawna ale dopiero w wersji 4.0 został dodany jako standardowa klasa w .NET Framework.