Xamarin.Forms Visual State ManagerThe Xamarin.Forms Visual State Manager

コードから設定した visual state に基づく XAML 要素を変更するには、Visual State Manager を使用します。Use the Visual State Manager to make changes to XAML elements based on visual states set from code.

Visual State Manager (VSM) は、Xamarin.Forms 3.0 の新機能です。The Visual State Manager (VSM) is new in Xamarin.Forms 3.0. VSM は、コードからユーザーインターフェースに視覚的変更を加える構造化された方法を提供します。The VSM provides a structured way to make visual changes to the user interface from code. ほとんどの場合、アプリケーションのユーザーインターフェイスは XAML で定義され、この XAML には Visual State Manager がユーザーインターフェイスの表示に影響を与える方法を記述するマークアップが含まれています。In most cases, the user interface of the application is defined in XAML, and this XAML includes markup describing how the Visual State Manager affects the visuals of the user interface.

VSM は、visual state という概念を導入しています。The VSM introduces the concept of visual states. Button などの Xamarin.Forms のビューは、基になる状態 —(無効な状態か、押されているか、入力フォーカスを持つか) に応じていくつかの異なる視覚的外観を持つことができます。A Xamarin.Forms view such as a Button can have several different visual appearances depending on its underlying state — whether it's disabled, or pressed, or has input focus. これらが、ボタンの状態です。These are the button's states.

Visual state は、visual state groups にまとめられます。Visual states are collected in visual state groups. すべての visual state group 内の visual state は、相互に排他的です。All the visual states within a visual state group are mutually exclusive. visual state と visual state group の両方は、単純なテキスト文字列によって識別されます。Both visual states and visual state groups are identified by simple text strings.

Xamarin.Forms Visual State Manager では、3 つの visual state を使って "CommonStates" という名前の 1 つの visual state group を定義しています。The Xamarin.Forms Visual State Manager defines one visual state group named "CommonStates" with three visual states:

  • "Normal""Normal"
  • "Disabled""Disabled"
  • "Focused""Focused"

この visual state group は、 VisualElement から派生したすべてのクラスでサポートされています。VisualElement は、ViewPage の基本クラスです。This visual state group is supported for all classes that derive from VisualElement, which is the base class for View and Page.

この記事でも説明しますが、独自の visual state group や visual state を定義することもできます。You can also define your own visual state groups and visual states, as this article will demonstrate.

注意

トリガー をよく知る Xamarin.Forms 開発者は、トリガーもまたビューのプロパティの変更やイベントの発生に基づいてユーザーインターフェイスの見た目を変更をすることができることを知っています。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. 歴史的に、Visual State Manager は、visual state の組み合わせに起因する混乱を軽減するために、Windows の XAML ベースの環境で導入されました。Historically, the Visual State Manager was introduced in Windows XAML-based environments to alleviate the confusion resulting from combinations of visual states. Visual State Manager を使うと、visual state group 内の visual state は、常に相互に排他的です。With the VSM, the visual states within a visual state group are always mutually exclusive. いかなるときも、グループ内の1つの状態だけが、現在の状態になります。At any time, only one state in each group is the current state.

Common stateThe common states

Visual State Manager を使用すると、XAML ファイルに、ビューが通常か無効か、または入力フォーカスを持っているかによってビューの外観を変更できるセクションを含めることができます。The Visual State Manager allows you to include sections in your XAML file that can change the visual appearance of a view if the view is normal, or disabled, or has the input focus. これらは common states と呼ばれます。These are known as the common states.

たとえば、ページ上に Entry ビューがあるとすると、Entry ビューの外観を次のような方法で変更したくなるでしょう。For example, suppose you have an Entry view on your page, and you want the visual appearance of the Entry to change in the following ways:

  • Entry 、Pink」と入力する必要がありますがあるときにバック グラウンド、Entryは無効です。The Entry should have a pink background when the Entry is disabled.
  • Entry は、通常時はライムの背景を持つ。The Entry should have a lime background normally.
  • Entryは、入力フォーカスがある場合、通常の高さの 2 倍に拡大する。The Entry should expand to twice its normal height when it has input focus.

個々のビューに VSM マークアップを記述することができます。また複数のビューに適用する場合は、スタイルで定義することもできます。You can attach the VSM markup to an individual view, or you can define it in a style if it applies to multiple views. 次の 2 つのセクションでは、これらの方法について説明します。The next two sections describe these approaches.

ビューのマークアップを VSMVSM markup on a view

Entry ビューに VSM マークアップを付加するには、まず Entry を開始タグと終了タグに分割します。To attach VSM markup to an Entry view, first separate the Entry into start and end tags:

<Entry FontSize="18">

</Entry>

状態のいずれかが使用されるため、明示的なフォント サイズ与え、FontSizeプロパティ内のテキストのサイズを 2 倍にする、Entryします。It's given an explicit font size because one of the states will use the FontSize property to double the size of the text in the Entry.

次に、VisualStateManager.VisualStateGroups タグをこれらのタグの間に挿入します。Next, insert VisualStateManager.VisualStateGroups tags between those tags:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>

    </VisualStateManager.VisualStateGroups>
</Entry>

VisualStateGroups は、VisualStateManager クラスによって定義された、バインド可能な添付プロパティです。VisualStateGroups is an attached bindable property defined by the VisualStateManager class. (バインド可能な添付プロパティの詳細については、添付プロパティ の記事を参照してください)。このようにして、Entry オブジェクトに VisualStateGroups プロパティを添付します。(For more information on attached bindable properties, see the article Attached properties.) This is how the VisualStateGroups property is attached to the Entry object.

VisualStateGroups プロパティの型はVisualStateGroupList で、VisualStateGroup オブジェクトのコレクションです。The VisualStateGroups property is of type VisualStateGroupList, which is a collection of VisualStateGroup objects. VisualStateManager.VisualStateGroups タグ内に、含めたい visual state の各グループの VisualStateGroup タグを挿入します。Within the VisualStateManager.VisualStateGroups tags, insert a pair of VisualStateGroup tags for each group of visual states you wish to include:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">

        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

VisualStateGroup タグに、グループの名前を示す x:Name 属性があることに注目してください。Notice that the VisualStateGroup tag has an x:Name attribute indicating the name of the group. VisualStateGroup クラスには、x:Name の代わりに使用できる Name プロパティも定義されています。The VisualStateGroup class defines a Name property that you can use instead:

<VisualStateGroup Name="CommonStates">

x:NameName のどちらも使用することができますが、同じ要素に両方を使うことはできません。You can use either x:Name or Name but not both in the same element.

VisualStateGroup クラスは、States という名前のプロパティが定義されていて、それは VisualState オブジェクトのコレクションです。The VisualStateGroup class defines a property named States, which is a collection of VisualState objects. States は、VisualStateGroupsContent プロパティ なので、VisualStateGroup タグの間に直接 VisualState タグを含めることができます。States is the content property of VisualStateGroups so you can include the VisualState tags directly between the VisualStateGroup tags. (Content プロパティについては、重要な XAML 構文 の記事で説明します)。(Content properties are discussed in the article Essential XAML Syntax.)

次の手順では、グループ内のすべての表示状態にタグのペアを追加します。The next step is to include a pair of tags for every visual state in that group. これらのタグは、x:Name または Name を使用して識別することもできます。These also can be identified using x:Name or Name:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">

            </VisualState>

            <VisualState x:Name="Focused">

            </VisualState>

            <VisualState x:Name="Disabled">

            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

VisualState は、Setters という名前のプロパティを定義します。これは Setter オブジェクトのコレクションです。VisualState defines a property named Setters, which is a collection of Setter objects. これらは Style オブジェクトで使用するものと同じ Setter オブジェクトです。These are the same Setter objects that you use in a Style object.

SettersVisualState のコンテンツ プロパティでは ない ので、Setters プロパティのための要素タグを含める必要があります。Setters is not the content property of VisualState, so it is necessary to include property element tags for the Setters property:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="Normal">
                <VisualState.Setters>

                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Focused">
                <VisualState.Setters>
    
                </VisualState.Setters>
            </VisualState>

            <VisualState x:Name="Disabled">
                <VisualState.Setters>

                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

これで、1 つまたは複数の Setter オブジェクトを Setters タグの各ペアの間に挿入できるようになりました。You can now insert one or more Setter objects between each pair of Setters tags. これらは、前述した表示状態を定義する Setter オブジェクトです。These are the Setter objects that define the visual states described earlier:

<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>

Setter タグは、その状態が最新のときの特定のプロパティ値を示します。Each Setter tag indicates the value of a particular property when that state is current. Setter オブジェクトによって参照されるすべてのプロパティは、バインド可能なプロパティでバックアップする必要があります。Any property referenced by a Setter object must be backed by a bindable property.

このようなマークアップは、VsmDemos サンプルプログラムの VSM on View ページの基礎となっています。Markup similar to this is the basis of the VSM on View page in the VsmDemos sample program. このページには、3 つの Entry ビューが含まれていますが、2 番目の Entry にのみ VSM マークアップが付加されています。The page includes three Entry views, but only the second one has the VSM markup attached to it:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:VsmDemos"
             x:Class="VsmDemos.MainPage"
             Title="VSM Demos">

    <StackLayout>
        <StackLayout.Resources>
            <Style TargetType="Entry">
                <Setter Property="Margin" Value="20, 0" />
                <Setter Property="FontSize" Value="18" />
            </Style>

            <Style TargetType="Label">
                <Setter Property="Margin" Value="20, 30, 20, 0" />
                <Setter Property="FontSize" Value="Large" />
            </Style>
        </StackLayout.Resources>

        <Label Text="Normal Entry:" />

        <Entry />

        <Label Text="Entry with VSM: " />

        <Entry>
            <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.Triggers>
                <DataTrigger TargetType="Entry"
                             Binding="{Binding Source={x:Reference entry3},
                                               Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Entry.Triggers>
        </Entry>

        <Label Text="Entry to enable 2nd Entry:" />

        <Entry x:Name="entry3"
               Text=""
               Placeholder="Type something to enable 2nd Entry" />
    </StackLayout>
</ContentPage>

2 つ目の Entry には Trigger コレクション の一部として DataTrigger が含まれることに注目してください。Notice that the second Entry also has a DataTrigger as part of its Trigger collection. これにより、この Entry は、3 番目の Entry に何かが入力されるまで無効になります。This causes the Entry to be disabled until something is typed into the third Entry. iOS、Android、およびユニバーサル Windows プラットフォーム (UWP) で実行されている起動時のページを次に示します。Here's the page at startup running on iOS, Android, and the Universal Windows Platform (UWP):

VSM on View: 無効VSM on View: Disabled

現在の表示状態が「無効」のため、iOS および Android の画面では、2 番目の Entry の背景はピンクになっています。The current visual state is "Disabled" so the background of the second Entry is pink on the iOS and Android screens. Entry の UWP 実装では、Entry が無効の場合に背景色を設定することができません。The UWP implementation of Entry does not allow setting the background color when the Entry is disabled.

3 つ目の Entry にいくつかのテキストを入力すると、2 つ目の Entry は「標準」の状態に切り替わり、背景が黄緑色になります。When you enter some text into the third Entry, the second Entry switches into the "Normal" state, and the background is now lime:

VSM on View: 標準VSM on View: Normal

2 つ目の Entry に触れると、入力フォーカスを取得します。When you touch the second Entry, it gets the input focus. これにより、「優先」の状態に切り替わり、高さが 2 倍に拡大されます。It switches to the "Focused" state and expands to twice its height:

VSM on View: 優先VSM on View: Focused

Entry が入力フォーカスを取得すると、黄緑色の背景は保持されないことに注意してください。Notice that the Entry does not retain the lime background when it gets the input focus. Visual State Manager は、表示状態の間で切り替わるため、以前の状態によって設定されたプロパティはリセットされます。As the Visual State Manager switches between the visual states, the properties set by the previous state are unset. 状態表示は相互に排他的であることを覚えておいてください。Keep in mind that the visual states are mutually exclusive. 「標準」状態は、Entry が単に有効であるというだけではありません。The "Normal" state does not mean solely that the Entry is enabled. それは、Entry が有効でかつ入力フォーカスがないということを意味します。It means that the Entry is enabled and does not have input focus.

Entry を「優先」の状態で黄緑色の背景のままにするには、表示状態に別の Setter を追加します。If you want the Entry to have a lime background in the "Focused" state, add another Setter to that visual state:

<VisualState x:Name="Focused">
    <VisualState.Setters>
        <Setter Property="FontSize" Value="36" />
        <Setter Property="BackgroundColor" Value="Lime" />
    </VisualState.Setters>
</VisualState>

これらの Setter オブジェクトを正常に機能させるには、VisualStateGroup に、グループ内の全ての状態の VisualState オブジェクトが含まれている必要があります。In order for these Setter objects to work properly, a VisualStateGroup must contain VisualState objects for all the states in that group. Setter オブジェクトが含まれていない表示状態がある場合は、空のタグとして含めます。If there is a visual state that does not have any Setter objects, include it anyway as an empty tag:

<VisualState x:Name="Normal" />

Style での Visual State Manager マークアップVisual State Manager markup in a style

2 つ以上のビューの間では、同じ Visual State Manager マークアップを共有することがしばしば必要になります。It's often necessary to share the same Visual State Manager markup among two or more views. このような場合、Style 定義にマークアップを配置することができます。In this case, you'll want to put the markup in a Style definition.

以下に、VSM On View ページ内にある Entry 要素のための既存の暗黙的な Style を示します。Here's the existing implicit Style for the Entry elements in the VSM On View page:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
</Style> 

VisualStateManager.VisualStateGroups 添付プロパティのための Setter タグを追加します。Add Setter tags for the VisualStateManager.VisualStateGroups attached bindable property:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">

    </Setter>
</Style> 

Setter のコンテンツ プロパティは Value なので、Value プロパティの値は、このタグ内に直接指定できます。The content property for Setter is Value, so the value of the Value property can be specified directly within those tags. このプロパティの型は、VisualStateGroupList です。That property is of type VisualStateGroupList:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>

        </VisualStateGroupList>
    </Setter>
</Style> 

これらのタグに 1 つ以上の VisualStateGroup オブジェクトを含めることができます。Within those tags you can include one of more VisualStateGroup objects:

<Style TargetType="Entry">
    <Setter Property="Margin" Value="20, 0" />
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">

            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style> 

VSM マークアップの残りの部分では前に、と同じです。The remainder of the VSM markup is the same as before.

ここでは、VSM in Style ページの完全な VSM マークアップを示します。Here's the VSM in Style page showing the complete VSM markup:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmInStylePage"
             Title="VSM in Style">
    <StackLayout>
        <StackLayout.Resources>
            <Style TargetType="Entry">
                <Setter Property="Margin" Value="20, 0" />
                <Setter Property="FontSize" Value="18" />
                <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" />
                                    <Setter Property="BackgroundColor" Value="Lime" />
                                </VisualState.Setters>
                            </VisualState>

                            <VisualState x:Name="Disabled">
                                <VisualState.Setters>
                                    <Setter Property="BackgroundColor" Value="Pink" />
                                </VisualState.Setters>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateGroupList>
                </Setter>
            </Style>

            <Style TargetType="Label">
                <Setter Property="Margin" Value="20, 30, 20, 0" />
                <Setter Property="FontSize" Value="Large" />
            </Style>
        </StackLayout.Resources>

        <Label Text="Normal Entry:" />

        <Entry />
        
        <Label Text="Entry with VSM: " />

        <Entry>
            <Entry.Triggers>
                <DataTrigger TargetType="Entry"
                             Binding="{Binding Source={x:Reference entry3},
                                               Path=Text.Length}"
                             Value="0">
                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Entry.Triggers>
        </Entry>

        <Label Text="Entry to enable 2nd Entry:" />

        <Entry x:Name="entry3"
               Text=""
               Placeholder="Type something to enable 2nd Entry" />
    </StackLayout>
</ContentPage>

現在このページのすべての Entry ビューは、それぞれの表示状態に対して同じ反応をします。Now all the Entry views on this page respond the same way to their visual states. 「優先」状態には、各 Entry が入力フォーカスを持っている時、黄緑色の背景に変わる 2 つ目の Setter が含まれていることにも注意してください。Notice also that the "Focused" state now includes a second Setter that gives each Entry a lime background also when it has input focus:

VSM in StyleVSM in Style

独自の表示状態を定義するDefining your own visual states

VisualElement から派生したすべてのクラスは、3 つの一般的な状態「標準」、「優先」、「無効」をサポートしています。Every class that derives from VisualElement supports the three common states "Normal", "Focused", and "Disabled". 内部的には、VisualElement クラスは、いつ有効/無効または優先/非優先になるかを検出し、静的なVisualStateManager.GoToState メソッドを呼び出します。Internally, the VisualElement class detects when it's becoming enabled or disabled, or focused or unfocused, and calls the static VisualStateManager.GoToState method:

VisualStateManager.GoToState(this, "Focused");

VisualElement クラス内で見つけることのできる Visual State Manager のコードはこれだけです。This is the only Visual State Manager code that you'll find in the VisualElement class. GoToState は、VisualElement から派生するすべてのクラスに基づくすべてのオブジェクトのために呼び出されるため、どの VisualElement オブジェクトからも Visual State Manager を使用して、変更に対応することができます。Because GoToState is called for every object based on every class that derives from VisualElement, you can use the Visual State Manager with any VisualElement object to respond to these changes.

興味深いことに、"CommonStates" という表示状態グループの名前は、VisualElement 内で明示的に参照されていません。Interestingly, the name of the visual state group "CommonStates" is not explicitly referenced in VisualElement. グループ名は、Visual State Manager 用の API の一部ではありません。The group name is not part of the API for the Visual State Manager. これまでに示した 2 つのサンプルプログラムの 1 つでは、グループ名を "CommonStates" から別の名前に変更することができ、そのプログラムは引き続き動作します。Within one of the two sample program shown so far, you can change the name of the group from "CommonStates" to anything else, and the program will still work. グループ名は、単にそのグループ内の状態の一般的な説明にすぎません。The group name is merely a general description of the states in that group. どのグループの表示状態も相互に排他的であるということが暗黙的に認識されます。いつでもただ 1 つの状態だけが現在の状態になります。It is implicitly understood that the visual states in any group are mutually exclusive: One state and only one state is current at any time.

独自の表示状態を実装する場合は、コードから VisualStateManager.GoToState を呼び出す必要があります。If you want to implement your own visual states, you'll need to call VisualStateManager.GoToState from code. ほとんどの場合、ページクラスの分離コードファイルからこのメソッドを呼ぶことになるでしょう。Most often you'll make this call from the code-behind file of your page class.

VsmDemos サンプルの VSM Validation ページは、入力検証に関連する Visual State Manager の使用方法を示します。The VSM Validation page in the VsmDemos sample shows how to use the Visual State Manager in connection with input validation. 以下の XAML ファイルには、EntryButton の 2 つのLabel 要素が含まれています。The XAML file consists of two Label elements, an Entry, and Button:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmValidationPage"
             Title="VSM Validation">
    <StackLayout Padding="10, 10">
        
        <Label Text="Enter a U.S. phone number:"
               FontSize="Large" />

        <Entry Placeholder="555-555-5555"
               FontSize="Large"
               Margin="30, 0, 0, 0"
               TextChanged="OnTextChanged" />

        <Label x:Name="helpLabel"
               Text="Phone number must be of the form 555-555-5555, and not begin with a 0 or 1">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidityStates">
                    
                    <VisualState Name="Valid">
                        <VisualState.Setters>
                            <Setter Property="TextColor" Value="Transparent" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState Name="Invalid" />
                    
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Label>

        <Button x:Name="submitButton"
                Text="Submit"
                FontSize="Large"
                Margin="0, 20"
                VerticalOptions="Center"
                HorizontalOptions="Center">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidityStates">
                    
                    <VisualState Name="Valid" />

                    <VisualState Name="Invalid">
                        <VisualState.Setters>
                            <Setter Property="IsEnabled" Value="False" />
                        </VisualState.Setters>
                    </VisualState>
                    
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Button>
    </StackLayout>
</ContentPage>

VSM マークアップは、2 番目の (helpLabel という名前の) Label と (submitButton という名前の) Button に添付されています。VSM markup is attached to the second Label (named helpLabel) and the Button (named submitButton). 「有効」と「無効」という名前の 2 つの相互に排他的な状態があります。There are two mutually-exclusive states, named "Valid" and "Invalid". これら 2 つの "ValidationState" グループのそれぞれで、「有効」と「無効」の両方に VisualState タグが含まれていますが、それぞれのケースでそのうちの 1 つは空になっていることに注意してください。Notice that each of the two "ValidationState" groups contains VisualState tags for both "Valid" and "Invalid", although one of them is empty in each case.

Entry に有効な電話番号が含まれない場合、現在の状態は「無効」になるので、 2 番目の Label が表示され、Button は無効になります。If the Entry does not contain a valid phone number, then the current state is "Invalid", and so the second Label is visible and the Button is disabled:

VSM Validation: Invalid StateVSM Validation: Invalid State

有効な電話番号を入力すると、現在の状態は「有効」になります。When a valid phone number is entered, then the current state becomes "Valid". 2 番目の Entry は消えて、Button が有効になります。The second Entry disappears and the Button is now enabled:

VSM Validation: Valid StateVSM Validation: Valid State

分離コード ファイルは、役割の処理を担います、TextChangedからイベントをEntryします。The code-behind file is reponsible for handling the TextChanged event from the Entry. ハンドラーは、入力文字列が有効かどうかを判断するのに正規表現を使用します。The handler uses a regular expression to determine if the input string is valid or not. という名前の分離コード ファイル内のメソッドGoToState呼び出す静的VisualStateManager.GoToState両方のメソッドhelpLabelsubmitButton:The method in the code-behind file named GoToState calls the static VisualStateManager.GoToState method for both helpLabel and submitButton:

public partial class VsmValidationPage : ContentPage
{
    public VsmValidationPage ()
    {
        InitializeComponent ();

        GoToState(false);
    }

    void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        bool isValid = Regex.IsMatch(args.NewTextValue, @"^[2-9]\d{2}-\d{3}-\d{4}$");
        GoToState(isValid);
    }

    void GoToState(bool isValid)
    {
        string visualState = isValid ? "Valid" : "Invalid";
        VisualStateManager.GoToState(helpLabel, visualState);
        VisualStateManager.GoToState(submitButton, visualState);
    }
}

また、GoToStateメソッドは、状態を初期化するコンス トラクターから呼び出されます。Notice also that the GoToState method is called from the constructor to initialize the state. 現在の状態が常にする必要があります。There should always be a current state. それに遠く及ばず、コードでがあります、表示状態グループの名前への参照わかりやすくするための目的で"ValidationStates"として、XAML で参照されています。But nowhere in the code is there any reference to the name of the visual state group, although it's referenced in the XAML as "ValidationStates" for purposes of clarity.

呼び出すと、これらの表示状態で、分離コード ファイルに影響を受けるページ上のすべてのオブジェクトのアカウントを実行する必要がありますに注意してください。VisualStateManager.GoToStateのこれらの各オブジェクト。Notice that the code-behind file must take account of every object on the page that is affected by these visual states, and to call VisualStateManager.GoToState for each of these objects. この例では 2 つのオブジェクト (、 LabelButton)、いくつかが考えられますが、詳細。In this example, it's only two objects (the Label and the Button), but it could be several more.

疑問に思うかもしれません。 場合は、分離コード ファイルには、これらの表示状態の影響を受けるページのすべてのオブジェクトを参照する必要があります、分離コード ファイルだけにアクセスできない理由オブジェクト直接でしょうか。You might wonder: If the code-behind file must reference every object on the page that is affected by these visual states, why can't the code-behind file simply access the objects directly? これは間違いでした。It surely could. ただし、VSM を使用する利点は、どの視覚的要素を制御できることだけで、XAML UI 設計のすべてを 1 つの場所に保持する別の状態に対応します。However, the advantage of using the VSM is that you can control how visual elements react to different state entirely in XAML, which keeps all of the UI design in one location. 視覚的な外観の設定を視覚的要素を分離コードから直接アクセスすることによって回避できます。This avoids setting visual appearance by accessing visual elements directly from the code-behind.

クラスを派生することが検討される可能性がありますEntryとおそらく外部検証関数を設定できるプロパティを定義します。It might be tempting to consider deriving a class from Entry and perhaps defining a property that you can set to an external validation function. 派生したクラスEntryを呼び出して、VisualStateManager.GoToStateメソッド。The class that derives from Entry can then call the VisualStateManager.GoToState method. このスキームは場合にのみが正常に機能は、Entryされたさまざまな視覚的な状態の影響を受ける唯一のオブジェクト。This scheme would work fine, but only if the Entry were the only object affected by the different visual states. この例で、LabelButtonも影響します。In this example, a Label and a Button are also be affected. VSM マークアップに接続されているために、方法はありません、 Entry VSM マークアップでは、ビジュアルの状態に変更を別のオブジェクトから参照するには、その他のオブジェクトをこれらに接続 ページと方法がないその他のオブジェクトを制御します。There is no way for VSM markup attached to an Entry to control other objects on the page, and no way for VSM markup attached to these other objects to reference a change in visual state from another object.

アダプティブレイアウトのための Visual State Manager の使用Using the Visual State Manager for adaptive layout

スマート フォンで実行されているアプリケーションは、縦または横の縦横比とデスクトップで実行されている Xamarin.Forms プログラムに通常表示できます Xamarin.Forms と多くのさまざまなサイズと縦横比を想定するサイズを変更できます。A Xamarin.Forms application running on a phone can usually be viewed in a portrait or landscape aspect ratio, and a Xamarin.Forms program running on the desktop can be resized to assume many different sizes and aspect ratios. 適切に設計されたアプリケーションがこれらのさまざまなページまたはウィンドウ フォーム ファクターの異なる方法では、そのコンテンツを表示します。A well-designed application might display its content differently for these various page or window form factors.

この手法とも呼ばれる_アダプティブ レイアウト_します。This technique is sometimes known as adaptive layout. アダプティブ レイアウトには、プログラムのビジュアルのみが含まれる、ため、Visual State Manager の理想的なアプリケーションになります。Because adaptive layout solely involves a program's visuals, it is an ideal application of the Visual State Manager.

簡単な例は、アプリケーションのコンテンツに影響するボタンの小規模なコレクションを表示するアプリケーションです。A simple example is an application that displays a small collection of buttons that affect the application's content. 縦モードの場合でこれらのボタンをページ上部にある水平方向の行で表示される可能性があります。In portrait mode, these buttons might be displayed in a horizontal row on the top of the page:

VSM アダプティブ レイアウト: 縦VSM Adaptive Layout: Portrait

横モードでボタンの配列が 1 つの側に移動され、列に表示。In landscape mode, the array of buttons might be moved to one side, and displayed in a column:

VSM アダプティブ レイアウト: 横VSM Adaptive Layout: Landscape

上から下に、プログラムはユニバーサル Windows プラットフォーム、Android、iOS で実行されています。From top to bottom, the program is running on the Universal Windows Platform, Android, and iOS.

VsmDemos サンプルの VSM Adaptive Layout ページでは、「縦」と「横」という名前の 2 つの表示状態を持った "OrientationStates" という名前のグループが定義されています。The VSM Adaptive Layout page in the VsmDemos sample defines a group named "OrientationStates" with two visual states named "Portrait" and "Landscape". (より複雑なアプローチでは、様々な異なるページやウィンドウの幅に基づくことがあります。)(A more complex approach might be based on several different page or window widths.)

VSM マークアップは、XAML ファイルの 4 箇所 で発生します。VSM markup occurs in four places in the XAML file. StackLayout という名前の mainStack は、Image 要素であるメニューとコンテンツの両方を含みます。The StackLayout named mainStack contains both the menu and the content, which is an Image element. この StackLayout は、縦向きモード時に垂直方向で、横向きモードに水平方向である必要があります。This StackLayout should have a vertical orientation in portrait mode and a horizontal orientation in landscape mode:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmAdaptiveLayoutPage"
             Title="VSM Adaptive Layout">

    <StackLayout x:Name="mainStack">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="OrientationStates">

                <VisualState Name="Portrait">
                    <VisualState.Setters>
                        <Setter Property="Orientation" Value="Vertical" />
                    </VisualState.Setters>
                </VisualState>

                <VisualState Name="Landscape">
                    <VisualState.Setters>
                        <Setter Property="Orientation" Value="Horizontal" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        
        <ScrollView x:Name="menuScroll">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="OrientationStates">
                    
                    <VisualState Name="Portrait">
                        <VisualState.Setters>
                            <Setter Property="Orientation" Value="Horizontal" />
                        </VisualState.Setters>
                    </VisualState>

                    <VisualState Name="Landscape">
                        <VisualState.Setters>
                            <Setter Property="Orientation" Value="Vertical" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
            
            <StackLayout x:Name="menuStack">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup Name="OrientationStates">

                        <VisualState Name="Portrait">
                            <VisualState.Setters>
                                <Setter Property="Orientation" Value="Horizontal" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState Name="Landscape">
                            <VisualState.Setters>
                                <Setter Property="Orientation" Value="Vertical" />
                            </VisualState.Setters>
                        </VisualState>
                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>

                <StackLayout.Resources>
                    <Style TargetType="Button">
                        <Setter Property="VisualStateManager.VisualStateGroups">
                            <VisualStateGroupList>
                                <VisualStateGroup Name="OrientationStates">

                                    <VisualState Name="Portrait">
                                        <VisualState.Setters>
                                            <Setter Property="HorizontalOptions" Value="CenterAndExpand" />
                                            <Setter Property="Margin" Value="10, 5" />
                                        </VisualState.Setters>
                                    </VisualState>

                                    <VisualState Name="Landscape">
                                        <VisualState.Setters>
                                            <Setter Property="VerticalOptions" Value="CenterAndExpand" />
                                            <Setter Property="HorizontalOptions" Value="Center" />
                                            <Setter Property="Margin" Value="10" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateGroupList>
                        </Setter>
                    </Style>
                </StackLayout.Resources>

                <Button Text="Banana"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="Banana.jpg" />
                
                <Button Text="Face Palm"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="FacePalm.jpg" />
                
                <Button Text="Monkey"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="monkey.png" />
                
                <Button Text="Seated Monkey"
                        Command="{Binding SelectedCommand}"
                        CommandParameter="SeatedMonkey.jpg" />
            </StackLayout>
        </ScrollView>

        <Image x:Name="image"
               VerticalOptions="FillAndExpand"
               HorizontalOptions="FillAndExpand" />
    </StackLayout>
</ContentPage>

ScrollView という名前の内側の menuScrollStackLayout という名前の menuStack は、ボタンのメニューを実装しています。The inner ScrollView named menuScroll and the StackLayout named menuStack implement the menu of buttons. これらのレイアウトの方向は、mainStack とは反対になっています。The orientation of these layouts is opposite of mainStack. メニューは縦向きモードでは水平に、横向きモードでは垂直である必要があります。The menu should be horizontal in portrait mode and vertical in landscape mode.

VSM マークアップの 4 番目のセクションは、ボタン自体の暗黙的なスタイルが使用されています。The fourth section of VSM markup is in an implicit style for the buttons themselves. このマークアップは、縦向きと横向きに固有の VerticalOptionsHorizontalOptions、および Margin プロパティを設定します。This markup sets VerticalOptions, HorizontalOptions, and Margin properties specific to the portait and landscape orientations.

分離コードファイルでは、BindingContextmenuStack プロパティを Button のコマンドを実装するように設定し、ハンドラーをページの SizeChanged イベントも付加します。The code-behind file sets the BindingContext property of menuStack to implement Button commanding, and also attaches a handler to the SizeChanged event of the page:

public partial class VsmAdaptiveLayoutPage : ContentPage
{
    public VsmAdaptiveLayoutPage ()
    {
        InitializeComponent ();

        SizeChanged += (sender, args) =>
        {
            string visualState = Width > Height ? "Landscape" : "Portrait";
            VisualStateManager.GoToState(mainStack, visualState);
            VisualStateManager.GoToState(menuScroll, visualState);
            VisualStateManager.GoToState(menuStack, visualState);

            foreach (View child in menuStack.Children)
            {
                VisualStateManager.GoToState(child, visualState);
            }
        };

        SelectedCommand = new Command<string>((filename) =>
        {
            image.Source = ImageSource.FromResource("VsmDemos.Images." + filename);
        });

        menuStack.BindingContext = this;
    }

    public ICommand SelectedCommand { private set; get; }
}

SizeChanged ハンドラーは、2 つの StackLayoutScrollView 要素に対して VisualStateManager.GoToState を呼び、その後 menuStack の子をループして Button 要素に対して VisualStateManager.GoToState を呼び出します。The SizeChanged handler calls VisualStateManager.GoToState for the two StackLayout and ScrollView elements, and then loops through the children of menuStack to call VisualStateManager.GoToState for the Button elements.

分離コードファイルで、XAML ファイルの要素のプロパティを設定すれば、より直接的に向きの変更を処理できるかのように思われるかもしれませんが、Visual State Manager は間違いなくより構造化されたアプローチです。It may seem as if the code-behind file can handle orientation changes more directly by setting properties of elements in the XAML file, but the Visual State Manager is definitely a more structured approach. 全てのビジュアルは XAML ファイル内で保持されるため、調査、メンテナンス、変更がしやすくなります。All the visuals are kept in the XAML file, where they become easier to examine, maintain, and modify.

Xamarin.University で visual State ManagerVisual State Manager with Xamarin.University

Xamarin.Forms 3.0 Visual State Manager、Xamarin University 提供Xamarin.Forms 3.0 Visual State Manager, by Xamarin University