Xamarin.FormsのVisualStateManagerについてのメモ
VisualStateManager
Visual State Manager(VSM)はコントロールの見た目を予め数種類定義しておき,それを切り替えることができるようにする機能です.
VSMにはデフォルトの定義があり,それはCommonStates
というグループ名で定義されている.
このCommonStates
グループには以下の4種類の見た目の定義が含まれる.これはVisualElement
クラスを継承しているクラスであれば有効です.
- Normal
- Disabled
- Focused
- Selected
これらはそれぞれ,通常の見た目,利用不可のときの見た目,フォーカスがあたっているときの見た目,選択されているときの見た目,についての定義です.
何もしなければデフォルトの見た目が使用されますが,VSMを通じてこれらの定義を上書きすることで,求める見た目にすることができます.
例えば以下の例では,Entryに対してNormal,Forcus,Disableの3つの状態に対して見た目を変更しています.
<Entry FontSize="18"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Lime"/> </VisualState.Setters> </VisualState> <VisualState x:Name="Focused"> <VisualState.Setters> <Setter Property="FontSize" Value="36"/> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Pink"/> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </Entry>
これら基本的なVisualStateに加えてコントローラごとに固有のStateを持っていることもある.
Class | States | More Information |
---|---|---|
Button | Pressed | Button visual states |
CarouselView | DefaultItem, CurrentItem, PreviousItem, NextItem | CarouselView visual states |
ImageButton | Pressed | ImageButton visual states |
VisualElement | Normal, Disabled, Focused, Selected | Common states |
例えばButtonコントロールはPressed
というStateを追加で持っています.
同じ種類のコントロールに一律にVSMを定義したい場合
これはResourceDictionary
を使います.
例えば,ContentPage.Resources
内に以下のようにVSMを定義します.この時,StyleのターゲットをEntry
にしています.
<ContentPage.Resources> <ResourceDictionary> <Style TargetType="Entry"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Lime" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Focused"> <VisualState.Setters> <Setter Property="FontSize" Value="36" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Pink" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ResourceDictionary> </ContentPage.Resources>
これで,このページ内の全てのEntryにこの設定が適用されます.
以下の例には2つのEntryがありますが,それぞれDisableとNormalの定義が適用されます.
<StackLayout> <!-- Place new controls here --> <Label HorizontalOptions="Center" Text="Welcome to Xamarin.Forms!" VerticalOptions="CenterAndExpand" /> <Entry FontSize="18" IsEnabled="False" Placeholder="Entry1" /> <Entry FontSize="18" IsEnabled="True" Placeholder="Entry2" /> </StackLayout>
Styleに名前をつけてコントロール側で指定する
ターゲットのコントロールの外側でStyleを名前付きで定義し,それをターゲットのコントロール側で名前で指定します.
<ContentPage.Resources> <ResourceDictionary> <Style TargetType="Entry" x:Key="EntryStyle"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Lime" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Focused"> <VisualState.Setters> <Setter Property="FontSize" Value="36" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Pink" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ResourceDictionary> </ContentPage.Resources>
コントロール側ではこんな感じです.
<Entry Style="{StaticResource EntryStyle}" FontSize="18" IsEnabled="False" Placeholder="Entry1" />
コードビハインドから切り替える
さて,上記のCommonStatesに関しては規定の4つの状態に対して再定義をして見た目を変更しましたが,それ以外の何か任意のトリガー(何かしらのイベント)によって見た目を切り替えたい場合はどうしたら良いでしょうか.
これはC#コード側の出番です.
XAMLでVisualStateを定義しておいて,
<StackLayout HorizontalOptions="Center" Orientation="Horizontal"> <StackLayout.Resources> <ResourceDictionary> <Style TargetType="Button"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="buttonState"> <VisualState x:Name="SelectedState"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Purple" /> <Setter Property="TextColor" Value="White" /> </VisualState.Setters> </VisualState> <VisualState x:Name="UnSelectedState"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Transparent" /> <Setter Property="TextColor" Value="Black" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ResourceDictionary> </StackLayout.Resources> <Button BackgroundColor="Transparent" Clicked="Button_Clicked" Text="Button1" /> <Button BackgroundColor="Transparent" Clicked="Button_Clicked" Text="Button2" /> <Button BackgroundColor="Transparent" Clicked="Button_Clicked" Text="Button3" /> </StackLayout>
それをC#コード側で以下のように適用します.
private void Button_Clicked(object sender, EventArgs e) { //最後に選択したボタンがNullじゃない if (_lastbutton != null) { VisualStateManager.GoToState(_lastbutton, "UnSelectedState"); } _lastbutton = (Button)sender; VisualStateManager.GoToState(_lastbutton, "SelectedState"); }
このようにGoToState
メソッドを使って,対象のコントロールとそれに適用したいVisualStateの名前を指定します.
クリックしたボタンが紫色になります.ラジオボタンのような動作ですが,このようにスタイルを切り替えることができます.
CommonStatesのStateを再定義するとき,空でもいい
以下のようにNormal
Stateの中身は空だが問題ない.
これはNormalが適用される状態になったときに,デフォルトの値が適用される.
じゃあ,いらないじゃない?というと,そうではない.
この例は,Buttonが押されたときにボタンが赤くなるが,もしNormalの指定がなければ赤くなったまま元の色には戻らない.
明示的に空のNormalを定義することで,押されている間だけ赤くなり,離されたらもとの色に戻る動作となる.
<Style TargetType="Button"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Pressed"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Red" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Normal" /> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style>
Triggerとの比較
状態に応じてプロパティの値を変更する
Xamarin.Forms developers familiar with triggers are aware that triggers can also make changes to visuals in the user interface based on changes in a view's properties or the firing of events. However, using triggers to deal with various combinations of these changes can become quite confusing. Historically, the Visual State Manager was introduced in Windows XAML-based environments to alleviate the confusion resulting from combinations of visual states. With the VSM, the visual states within a visual state group are always mutually exclusive. At any time, only one state in each group is the current state.