原文:The VisualStateManager and Triggerswindows
Author:Carole Snyder工具
Silverlight 推出了可視化狀態管理器( Visual State Manager ),它使得控件模版做者更方便地指定依賴可視化狀態的控件外觀。WPF Toolkit 附帶了一個可視化狀態管理器( Visual State Manager ),WPF 的下個版本將會加入 VSM。能夠理解地,VisualStateManager 的引進帶來了問題:什麼時候使用 VSM 代替觸發器,什麼時候使用觸發器會更適合。本博文嘗試解決這個問題:更多對如何使用可視化狀態管理器( VisualStateManager )的深刻討論在這裏。動畫
可視化狀態管理器( VisualStateManager )支持 parts 和 states 模型,控件做者使可視化狀態在控件模版中形式化的一種方法。可視化狀態管理器( VisualStateManager )使控件做者可以管理一個控件的不一樣狀態而且爲設計工具提供了一個方法,好比 Microsoft Blend 支持經過控件的可視化狀態自定義它的外觀。在 parts 和 states 模型被引進以前,廣泛地,當它改變了可視化狀態,控件模版做者使用觸發器來改變控件的外觀。下面的控件模版中,當鼠標懸停在按鈕上面或者按鈕被按壓時,使用觸發器來改變按鈕的 border 顏色。spa
<ControlTemplate TargetType="Button"> <Grid> <Ellipse Fill="{TemplateBinding BorderBrush}"/> <Ellipse x:Name="ButtonShape" Margin="5" Fill="{TemplateBinding Background}"/> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderBrush" Value="Cyan"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="BorderBrush" Value="Red"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>
在這個模型中,沒有關於什麼是可視化狀態正式的協議,這裏只有定義在控件裏的一些用來改變控件外觀的屬性。 A control that follows the parts and control model proactively communicates its visual states to control template authors. 當一個控件使用可視化狀態管理器( VisualStateManager )來改變它的可視化狀態,它就指望 控件模版(ControlTemplate )使用 可視化狀態管理器( VisualStateManager )來爲一個給定的可視化狀態指定控件的外觀。控件模版做者也能夠經過可視化過渡(VisualTransitions)使用自定義可視化狀態之間的過渡。可視化過渡(VisualTransitions)使得空間模版做者可以經過改變單個屬性改變的時間和區間對單個過渡(transition)進行微調,甚至對在其餘狀態中沒有說起的屬性加上動畫效果(animate)。下面的例子使用可視化狀態管理器( VisualStateManager )而不是觸發器來指定控件外觀的改變。.net
<ControlTemplate TargetType="Button"> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualStateGroup.Transitions> <!--Take one half second to transition to the MouseOver state.--> <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.5" /> </VisualStateGroup.Transitions> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Duration="0" Storyboard.TargetName="borderColor" Storyboard.TargetProperty="Color" To="Cyan"/> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ColorAnimation Duration="0" Storyboard.TargetName="borderColor" Storyboard.TargetProperty="Color" To="Red"/> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Ellipse> <Ellipse.Fill> <SolidColorBrush x:Name="borderColor" Color="Black"/> </Ellipse.Fill> </Ellipse> <Ellipse x:Name="ButtonShape" Margin="5" Fill="{TemplateBinding Background}"/> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </ControlTemplate>
控件經過使用可視化狀態管理器( VisualStateManager )並調用 VisualStateManager.GoToState 來改變可視化狀態。當這一切發生時,在可視化狀態(VisualState)和可視化過渡(VisualTransition)對象中, 可視化狀態管理器( VisualStateManager )正確地中止和開始故事板(storyboards),所以按鈕的外觀正確地改變可視化狀態。爲此,這裏有一個明確的分工:控件做者指定控件的哪一個可視化狀態並決定一個控件什麼時候進入每個可視化狀態;模版做者指定這個控件在每一個可視化狀態中的外觀。設計
然而觸發器在WPF中並非沒用了。你可使用觸發器改變控件的外觀,這並不對應與一個可視化狀態。舉個例子,按鈕有一個 IsDefault 屬性,可是在按鈕中沒有一個相對應的可視化狀態。控件模版做者可能想要指定依賴 IsDefault 的值的按鈕外觀,這是一個適合用觸發器的情景。下面的例子重複了以前的例子,並加入了一個觸發器來指定依賴 IsDefault 的值的按鈕外觀。code
<ControlTemplate TargetType="Button"> <Grid> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualStateGroup.Transitions> <!--Take one half second to transition to the MouseOver state.--> <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.5" /> </VisualStateGroup.Transitions> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Duration="0" Storyboard.TargetName="borderColor" Storyboard.TargetProperty="Color" To="Cyan"/> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ColorAnimation Duration="0" Storyboard.TargetName="borderColor" Storyboard.TargetProperty="Color" To="Red"/> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Ellipse> <Ellipse.Fill> <SolidColorBrush x:Name="borderColor" Color="Black"/> </Ellipse.Fill> </Ellipse> <Ellipse x:Name="defaultOutline" Stroke="{TemplateBinding Background}" StrokeThickness="2" Margin="2"/> <Ellipse x:Name="ButtonShape" Margin="5" Fill="{TemplateBinding Background}"/> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsDefault" Value="False"> <Setter TargetName="defaultOutline" Property="Stroke" Value="Transparent"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate>
按鈕中的 IsMouseOver 和 IsPressed 屬性依然有效,可是你不該該建立觸發器來阻止它們改變按鈕的外觀。這並不意味這些屬性沒有用了。這些屬性依然能夠被應用做者用來在代碼中檢查控件的可視化狀態,甚至當他們使用可視化狀態管理器(VisualStateManager)在它們的可視化狀態之間過渡時,控件做者應該繼續定義用於可視化狀態的屬性。orm