Selama ini fitur Visual State Manager (VSM) hanya terdapat di Silverlight. Setelah adanya WPF Toolkit fitur VSM dapat digunakan pada aplikasi WPF. Jika kita ingin menggunakan VSM di Expression Blend 2 maka terlebih dahulu harus install Blend SP1. WPF Toolkit dapat di download disini, sedangkan Blend SP1 dapat didownload disini.
Apa itu VSM? VSM merupakan fitur yang digunakan untuk menyimpan setting atau state visual dari sebuah object. Object ini dapat berupa Rectangle, Text, dll. Dengan VSM kita dapat membuat kontrol lebih hidup. Misalnya ketika sebuah button medapatkan event Focus kita ingin border nya berubah warna secara gradien, dan gradien tersebut berulang kembali ke warna awal. Hal ini dapat dilakukan dengan membuat Storyboard untuk animasi yang diinginkan berdasarkan timeline tertentu. VSM ini berguna kalau kita membuat user control atau custom control.
Mari kita buat contoh sederhana, yaitu Button dengan border brush dimana warnanya berubah secara linear gradient ke warna tertentu dan kembali ke warna asalnya. Tools yang akan saya gunakan untuk design control tersebut yaitu Blend2 yang telah diinstal SP1 nya dan WPF Toolkit, sedangkan Visual Studio 2008 digunakan untuk menambahkan event handler yang akan mengeksekusi state yang telah dibuat.
1. Setelah anda install WPF Toolkit dan Blend SP1, tambahkan entri dibawah ini ke dalam registry (Buat tipe DWORD dengan value 0 ) :
- 32 bit OS : HKLM/Software/Microsoft/Expression/Blend/EnableVSM
- 64 bit OS : HKLM/Software/Wow6432Node/Microsoft/Expression/Blend/EnableVSM
2. Create WPF application project dari Blend2.
3. Tambahkan referensi ke WPFToolkit.dll dari lokasi C:\Program Files\WPF Toolkit\v3.5.31016.1\WPFToolkit.dll...sesuaikan lokasinya berdasarkan lokasi instalasi di komputer anda.
4. Tutup semua window.xaml yang terbuka, kemudian buka kembali window nya.
5. Tambahkan sebuah control Button pada window.xaml.
6. Klik kanan button tersebut => pilih menu Edit Control Parts (Template) => Create Empty...template dapat disimpan di window atau di app.xaml :

7. Tambahkan sebuah Rectangle dan TextBlock, atur properti nya sehingga memiliki tampilan seperti dibawah ini :

Kode xaml untuk tampilan diatas :
1: <Application
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: x:Class="App"
5: StartupUri="Window1.xaml"
6: xmlns:vsm="clr-namespace:System.Windows;assembly=WPFToolkit">
7: <Application.Resources>
8: <!--
Resources scoped at the Application level should be defined here. -->
9: <ControlTemplate x:Key="VSMButtonStyle" TargetType="{x:Type Button}">
10: <Grid>
11: <Rectangle Margin="0,0,0,0" VerticalAlignment="Stretch"
12: RadiusX="30" RadiusY="30" StrokeThickness="4" x:Name="rectangle">
13: <Rectangle.Fill>
14: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
15: <GradientStop Color="#FF000000" Offset="0"/>
16: <GradientStop Color="#FFFFFFFF" Offset="1"/>
17: </LinearGradientBrush>
18: </Rectangle.Fill>
19: <Rectangle.Stroke>
20: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
21: <GradientStop Color="#FF0402FF" Offset="0"/>
22: <GradientStop Color="#FFDEFF00" Offset="1"/>
23: </LinearGradientBrush>
24: </Rectangle.Stroke>
25: </Rectangle>
26: <TextBlock Margin="0,0,0,0" Text="VSM Button!" TextWrapping="Wrap"
27: HorizontalAlignment="Center" VerticalAlignment="Center"
28: FontSize="24" x:Name="textBlock">
29: <TextBlock.Foreground>
30: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
31: <GradientStop Color="#FFFF1900" Offset="0"/>
32: <GradientStop Color="#FFECFF00" Offset="1"/>
33: </LinearGradientBrush>
34: </TextBlock.Foreground>
35: </TextBlock>
36: </Grid>
37: </ControlTemplate>
38: </Application.Resources>
39: </Application>
8. Cari window berikut ini :

dari window inilah kita akan menambahkan visual state.
9. Tambahkan Visual State Group pada window States (klik tanda panah pada ujung kanan atas windownya). Pada Visual State Group yang telah terbentuk buat visual state (klik tanda panah pada ujung kanan atas windownya), misalnya saya beri nama "FocusState" :

Pada saat itu juga state recording status nya "On", artinya segala sesuatu yang kita lakukan untuk perubahan terhadap properti object-object yang ada di window akan "direkam".
10. Pada window "Objects and Timeline", geser timeline dari 0 ke 1 :

kita akan lakukan perubahan gradien color untuk border rectangle dan textblock. Geser slider color untuk masing-masing object (border dan textblock) menjadi warna yang berlawanan dengan warna asalnya. Atur properti "AutoReverse" ke True dan "RepeatBehaviour" ke Forever pada properti window untuk state FocusState (untuk menampilkannya anda perlu klik terlebih dahulu state tersebut pada Object and Timeline window.

11. Tambahkan visual state baru pada VisualStateGroup yang ada, misal saya beri nama "LostFocusState" :

Pada state tersebut kita ga perlu lakukan perubahan setting properti apapun. State ini akan digunakan untuk membuat animasi yang telah dibuat berhenti ketika kontrol tersebut kehilangan event Focus.
Kode xaml untuk VisualStateGroup yang telah dibuat :
1: <Application
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: x:Class="App"
5: StartupUri="Window1.xaml" xmlns:vsm="clr-namespace:System.Windows;assembly=WPFToolkit">
6: <Application.Resources>
7: <!--
Resources scoped at the Application level should be defined here. -->
8: <ControlTemplate x:Key="VSMButtonStyle" TargetType="{x:Type Button}">
9: <Grid>
10: <vsm:VisualStateManager.VisualStateGroups>
11: <vsm:VisualStateGroup x:Name="VisualStateGroup">
12: <vsm:VisualState x:Name="FocusState">
13: <Storyboard AutoReverse="True" RepeatBehavior="Forever">
14: <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
15: Storyboard.TargetName="rectangle"
16: Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[0].(GradientStop.Offset)">
17: <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/>
18: </DoubleAnimationUsingKeyFrames>
19: <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
20: Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Stroke).(GradientBrush.GradientStops)[1].(GradientStop.Offset)">
21: <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
22: </DoubleAnimationUsingKeyFrames>
23: <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
24: Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(TextElement.Foreground).(GradientBrush.GradientStops)[1].(GradientStop.Offset)">
25: <SplineDoubleKeyFrame KeyTime="00:00:01" Value="0"/>
26: </DoubleAnimationUsingKeyFrames>
27: <DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
28: Storyboard.TargetName="textBlock" Storyboard.TargetProperty="(TextElement.Foreground).(GradientBrush.GradientStops)[0].(GradientStop.Offset)">
29: <SplineDoubleKeyFrame KeyTime="00:00:01" Value="1"/>
30: </DoubleAnimationUsingKeyFrames>
31: </Storyboard>
32: </vsm:VisualState>
33: <vsm:VisualState x:Name="LostFocusState"/>
34: </vsm:VisualStateGroup>
35: </vsm:VisualStateManager.VisualStateGroups>
12. Buka window.xaml design. Tampilkan list event untuk button kontrol tersebut pada properties window. Cari event GotFocus, double click pada textbox event tersebut sehingga muncul Visual Studio. Tambahkan kode berikut :
1: Private Sub btnVSM_GotFocus(ByVal sender As System.Object, _
2: ByVal e As System.Windows.RoutedEventArgs)
3:
4: VisualStateManager.GoToState(btnVSM, "FocusState", False)
5: End Sub
6:
7:
8: Private Sub btnVSM_LostFocus(ByVal sender As System.Object, _
9: ByVal e As System.Windows.RoutedEventArgs)
10:
11: VisualStateManager.GoToState(btnVSM, "LostFocusState", False)
12: End Sub
VisualStateManager class digunakan untuk memanggil atau eksekusi state yang diinginkan pada event tertentu. Parameter pertama diisi dengan nama kontrol buttonya, parameter kedua diisi dengan nama state yang akan dieksekusi, parameter ketiga jika kita ingin menggunakan state ini sebagai VisualTransition. Untuk contoh kasus ini saya tidak menambahkan transition time yang merupakan waktu jeda antara state-state tertentu.
Hasilnya dapat anda lihat berikut ini (border gradien color dan foreground textblock akan berubah-ubah terus selama button mendapatkan event focus) :