Demo下載:http://yunpan.cn/cFjgPtWRHKH9H 访问密码 c4b7
顧名思義,視圖狀態管理器。
在WPF中,它的功能似乎更強大。在UWP中,閹割了GotElementState方法,導致它只能在控件內部使用。
這個東東一般用來突出某些操作,以提醒用戶。它原來是狀態A,後來用戶進行了某些操作,我們就會根據用戶的操作,判斷他想要做什麼,然後根據他的目的,顯示狀態B。最容易理解的例子就是按鈕,它普通狀態,鼠標放上去以後,變成了另一種狀態,點擊又是另一種狀態。
1.按鈕狀態
我們先來看看,按鈕有哪些狀態。首先我們來編輯一個按鈕模板:XAML面板中,添加一個按鈕,並在它上面右鍵->編輯模板->編輯副本,截圖如下:
然後,我們就可以得到一個按鈕的副本:
<Style x:Key="ButtonStyle1" TargetType="Button"> <Setter Property="Background" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}"/> <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/> <Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundTransparentBrush}"/> <Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}"/> <Setter Property="Padding" Value="8,4,8,4"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/> <Setter Property="FontWeight" Value="Normal"/> <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/> <Setter Property="UseSystemFocusVisuals" Value="True"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid"> <Grid.Background> <SolidColorBrush x:Name="bkBrush" Color="LightGray" /> </Grid.Background> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <Storyboard> <PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/> </Storyboard> </VisualState> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseMediumLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="RootGrid"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseMediumLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightTransparentBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlHighlightBaseHighBrush}"/> </ObjectAnimationUsingKeyFrames> <PointerDownThemeAnimation Storyboard.TargetName="RootGrid"/> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="RootGrid"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlBackgroundBaseLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledBaseLowBrush}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SystemControlDisabledTransparentBrush}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <!------------- 我加的狀態 ---------------> <VisualState x:Name="testState"> <Storyboard> <ColorAnimation To="Red" Storyboard.TargetName="bkBrush" Storyboard.TargetProperty="Color" Duration="0:0:2" EnableDependentAnimation="True" /> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" ContentTemplate="{TemplateBinding ContentTemplate}" ContentTransitions="{TemplateBinding ContentTransitions}" Content="{TemplateBinding Content}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
在上面的代碼中,可以看到鼠標有 "Normal" "PointerOver" "Pressed" "Disabled" 四種狀態。意思一看就知道。
那麼這些狀態是怎麼執行的呢?由誰來執行呢?
為了解答這個問題,我添加了一個狀態 "testState"。我添加了一個按鈕,單擊來調用它。
private void button1_Click(object sender, RoutedEventArgs e) { Windows.UI.Xaml.VisualStateManager.GoToState(button, "testState", true); }
一點擊button1,就使用 GoToState 調用另一個按鈕的狀態。具體效果看我Demo.
那麼我們能調用 PointerOver之類的狀態嗎?答案是肯定的。它由誰來調用,答案也呼之欲出,Button來調用。Button是一個控件,我們只要捕獲鼠標消息,然後在消息處理函數那裡調用 GoToState, 那這一切不就順理成章了嗎?
2.UserControl
所以我又寫了一個UserControl,當鼠標放上去時,改變它的背景顏色。
<UserControl x:Class="VisualStateManager.MyUserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:VisualStateManager" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <UserControl.Resources> <SolidColorBrush x:Name="RedBrush" Color="Red" /> </UserControl.Resources> <Grid x:Name="rootGrid" PointerEntered="rootGrid_PointerEntered" Background="Gray" PointerExited="rootGrid_PointerExited"> <VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualStateGroup.States> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="rootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource RedBrush}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup.States> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Grid> </UserControl>
看Grid的消息響應:
PointerEntered="rootGrid_PointerEntered" Background="Gray" PointerExited="rootGrid_PointerExited"
private void rootGrid_PointerEntered(object sender, PointerRoutedEventArgs e) { Windows.UI.Xaml.VisualStateManager.GoToState(this, "PointerOver", true); } private void rootGrid_PointerExited(object sender, PointerRoutedEventArgs e) { Windows.UI.Xaml.VisualStateManager.GoToState(this, "Normal", true); }
然後效果就出來了,鼠標一放上去,背景顏色變紅。SO EASY。
但是,如果你仔細看,Normal 狀態,發現它什麼也不做,那為什麼它什麼也不做會還原為原來的灰色背景呢?
3.狀態執行的細節
我們就必須要了解這個細節了:
1. 如果控件要转换到的新State有Storyboard,运行该Storyboard。如果控件的旧State有Storyboard,结束其运行。
2. 如果控件已处于新state状态(即新旧State相同),则GoToState不执行任何操作并返回true。
3. 如果新State在控件的ControlTemplate中不存在,则GoToState不执行任何操作并返回 false。
以上細節摘自:http://www.cnblogs.com/KeithWang/archive/2012/03/30/2425588.html
4.觸發器自動跳轉狀態
據我研究,微軟只提供了窗口大小改變,然後觸發狀態。。感覺功能很局限啊。不知道有沒有大神知道更多的,然後偷偷地告訴我。
這個東東,最常用的就是當窗口大小改變時,改變程式的界面佈局。假設有一個圖片瀏覽工具,當窗口寬度為100的時候,你只顯示一列。當窗口寬度為200的時候,你就顯示兩列。
又如微軟的視頻顯示中的那樣,在手機中,操作按鈕放在程式最下方,在PC上,操作按鈕放在最右邊。。這個時候可以使用這個東東。。我寫了一個當窗口寬度大於600時,自動拉伸按鈕長度的狀態:
<VisualStateManager.VisualStateGroups> <VisualStateGroup> <VisualState> <VisualState.StateTriggers> <AdaptiveTrigger MinWindowWidth="600" /> </VisualState.StateTriggers> <VisualState.Setters> <Setter Target="button1.Width" Value="500" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups>
如果是改變按鈕的位置,如顯示在下方,改變到顯示到右方,可以看我的這篇博客:WIN10-UWP開發之控件。或許看微軟的示例。
好了,就寫到這裡了。