Xamarin.Forms Visual State Manager

サンプルのダウンロードサンプルのダウンロード

コードから設定されたビジュアル状態に基づいて XAML 要素を変更するには、Visual State Manager を使用します。

Visual State Manager (VSM) は、コードからユーザー インターフェイスに視覚的な変更を加える構造化された方法を提供します。 ほとんどの場合、アプリケーションのユーザー インターフェイスは XAML で定義されており、この XAML には、Visual State Manager がユーザー インターフェイスのビジュアルに与える影響を説明するマークアップが含まれています。

VSM では、 ビジュアル状態の概念が導入されています。 Xamarin.FormsなどのButtonビューには、基になる状態に応じて、無効か押されているか、入力フォーカスがあるかに応じて、いくつかの異なる外観を持つことができます。 これらはボタンの状態です。

ビジュアル状態は、 ビジュアル状態グループで収集されます。 ビジュアル状態グループ内のすべてのビジュアル状態は、相互に排他的です。 ビジュアル状態とビジュアル状態グループの両方が、単純なテキスト文字列によって識別されます。

Visual State Manager では Xamarin.Forms 、"CommonStates" という名前の 1 つのビジュアル状態グループが定義され、次のビジュアル状態が定義されます。

  • "Normal"
  • "無効"
  • "Focused"
  • "選択済み"

このビジュアル状態グループは、 および Pageの基底クラスである からVisualElement派生するすべてのクラスでViewサポートされています。

この記事で示すように、独自のビジュアル状態グループとビジュアル状態を定義することもできます。

注意

Xamarin.Formsトリガーに精通している開発者は、トリガーは、ビューのプロパティの変更やイベントの発生に基づいて、ユーザー インターフェイスのビジュアルに変更を加えることもできます。 ただし、トリガーを使用してこれらの変更のさまざまな組み合わせを処理すると、非常に混乱する可能性があります。 これまで、Visual State Manager は、視覚的な状態の組み合わせによる混乱を軽減するために、Windows XAML ベースの環境で導入されていました。 VSM では、ビジュアル状態グループ内のビジュアル状態は常に相互に排他的です。 常に、各グループ内の 1 つの状態のみが現在の状態になります。

一般的な状態

Visual State Manager を使用すると、ビューが正常であるか無効になっているか、入力フォーカスがある場合にビューの外観を変更できるマークアップを XAML ファイルに含めることができます。 これらは 一般的な状態と呼ばれます。

たとえば、ページにビューがあり Entry 、 の視覚的な外観を次の Entry 方法で変更するとします。

  • Entry 無効になっている場合、 にはピンク色の Entry 背景が必要です。
  • Entry 通常、ライムの背景を持つ必要があります。
  • Entry 、入力フォーカスがある場合、通常の高さの 2 倍に拡大する必要があります。

VSM マークアップを個々のビューにアタッチすることも、複数のビューに適用される場合はスタイルで定義することもできます。 次の 2 つのセクションでは、これらの方法について説明します。

ビューの VSM マークアップ

VSM マークアップをビューに Entry アタッチするには、最初に を Entry 開始タグと終了タグに分けます。

<Entry FontSize="18">

</Entry>

状態の 1 つが プロパティを使用 FontSize して 内のテキストのサイズを 2 倍にするため、明示的なフォント サイズが Entry与えられます。

次に、これらのタグの間にタグを挿入 VisualStateManager.VisualStateGroups します。

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

    </VisualStateManager.VisualStateGroups>
</Entry>

VisualStateGroups は、 クラスによって VisualStateManager 定義されたバインド可能な添付プロパティです。 (添付されたバインド可能なプロパティの詳細については、 添付プロパティに関する記事を参照してください)。これは、 プロパティを VisualStateGroups オブジェクトにアタッチする Entry 方法です。

プロパティの型VisualStateGroupListVisualStateGroups、 オブジェクトのVisualStateGroupコレクションです。 タグ内に VisualStateManager.VisualStateGroups 、含めるビジュアル状態のグループごとにタグの VisualStateGroup ペアを挿入します。

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

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

タグには VisualStateGroup 、グループの名前を x:Name 示す属性があることに注意してください。 クラスは VisualStateGroupName 代わりに使用できるプロパティを定義します。

<VisualStateGroup Name="CommonStates">

または をx:NameName使用できますが、両方を同じ要素で使用することはできません。

クラスは VisualStateGroup 、 オブジェクトのコレクションである という名前 StatesVisualState プロパティを定義します。 Statesは の content プロパティVisualStateGroupsであるため、タグ間VisualStateGroupにタグをVisualState直接含めることができます。 (コンテンツ プロパティについては、「 基本的な XAML 構文」を参照してください)。

次の手順では、そのグループ内のすべてのビジュアル状態に対してタグのペアを含めます。 これらは、 または Nameを使用してx: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 は、 オブジェクトのコレクションである という名前 SettersSetter プロパティを定義します。 これらは、オブジェクトで使用するのとStyle同じSetterオブジェクトです。

Setters は の content プロパティ ではありません 。そのため、 プロパティ VisualStateのプロパティ要素タグを Setters 含める必要があります。

<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 挿入できるようになりました。 前に説明した Setter ビジュアル状態を定義するオブジェクトを次に示します。

<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 タグは、その状態が現在の場合に特定のプロパティの値を示します。 オブジェクトによって参照されるすべてのプロパティは、 Setter バインド可能なプロパティによってサポートされている必要があります。

このようなマークアップは、VsmDemos サンプル プログラムの [表示] ページの VSM の基礎です。 ページには 3 つの Entry ビューが含まれていますが、VSM マークアップがアタッチされているのは 2 番目のビューのみです。

<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 の には、 DataTrigger コレクションの一部として も含まれることに注意してください Trigger 。 これにより、3 番目EntryEntry に何かが入力されるまで、 が無効になります。 iOS、Android、ユニバーサル Windows プラットフォーム (UWP) で実行されている起動時のページを次に示します。

ビューの VSM: ビュー

現在のビジュアル状態は "無効" であるため、iOS および Android 画面では 2 番目 Entry の画面の背景がピンク色になります。 の UWP 実装 Entry では、 が無効になっている場合に背景色を Entry 設定できません。

3 番目 Entryの にテキストを入力すると、2 番目 Entry のテキストが "標準" 状態に切り替わります。背景はライムになりました。

ビューの VSM: ビューの標準

2 つ目 Entryの をタッチすると、入力フォーカスが取得されます。 "Focused" 状態に切り替え、その高さの 2 倍に拡張されます。

VSM on View: ビューにフォーカスされた

Entryでは、入力フォーカスを取得してもライムの背景は保持されないことに注意してください。 Visual State Manager がビジュアル状態を切り替えるにつれて、前の状態によって設定されたプロパティは設定解除されます。 ビジュアルの状態は相互に排他的であることに注意してください。 "Normal" 状態は、 が有効であるという Entry 意味ではありません。 これは、 が Entry 有効であり、入力フォーカスがないことを意味します。

に "Focused" 状態のライム背景を含める場合 Entry は、そのビジュアル状態に別の Setter 背景を追加します。

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

これらの Setter オブジェクトが正しく機能するためには、 には、 VisualStateGroup そのグループ内のすべての状態のオブジェクトが含まれている VisualState 必要があります。 オブジェクトがない視覚的な状態 Setter がある場合は、空のタグとして含めます。

<VisualState x:Name="Normal" />

スタイルの Visual State Manager マークアップ

多くの場合、2 つ以上のビュー間で同じ Visual State Manager マークアップを共有する必要があります。 この場合は、マークアップを定義に Style 配置します。

VSM On View ページの要素に対するEntry既存の暗黙的Styleな情報を次に示します。

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

添付されたバインド可能なプロパティのVisualStateManager.VisualStateGroupsタグを追加Setterします。

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

    </Setter>
</Style>

の content プロパティ SetterValueであるため、これらのタグ内で Value プロパティの値を直接指定できます。 そのプロパティの型 VisualStateGroupListは です。

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

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

これらのタグ内には、次 VisualStateGroup のいずれかのオブジェクトを含めることができます。

<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 マークアップの残りの部分は、以前と同じです。

完全な VSM マークアップを示す VSM in Style ページを次に示します。

<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 このページのすべてのビューが、表示状態に対して同じ方法で応答するようになりました。 また、"Focused" 状態には、入力フォーカスがある場合にも各ライムの背景を与える Entry 2 つ目Setterが含まれていることに注意してください。

スタイルの VSM スタイル

の視覚的な状態 Xamarin.Forms

で定義されている視覚的な状態を次の表に Xamarin.Forms示します。

クラス 状態 詳細情報
Button Pressed ボタンの表示状態
CheckBox IsChecked CheckBox の表示状態
CarouselView DefaultItem, CurrentItem, PreviousItem, NextItem カルーセルビューの表示状態
ImageButton Pressed ImageButton のビジュアル状態
RadioButton Checked, Unchecked RadioButton の表示状態
Switch On, Off ビジュアルの状態を切り替える
VisualElement Normal, Disabled, Focused, Selected 一般的な状態

これらの各状態には、 という名前 CommonStatesのビジュアル状態グループを介してアクセスできます。

さらに、 は CollectionView 状態を Selected 実装します。 詳細については、「 選択した項目の色を変更する」を参照してください。

複数の要素に状態を設定する

前の例では、視覚的な状態が 1 つの要素にアタッチされ、操作されていました。 ただし、1 つの要素にアタッチされているが、同じスコープ内の他の要素にプロパティを設定する視覚的な状態を作成することもできます。 これにより、状態が動作する各要素で視覚的な状態を繰り返す必要がなくなります。

Setter型にはTargetName、 型stringの プロパティがあり、ビジュアル状態の が操作するSetterターゲット要素を表します。 プロパティをTargetName定義すると、 でSetter定義TargetNameされている要素の が にValue設定Propertyされます。

<Setter TargetName="label"
        Property="Label.TextColor"
        Value="Red" />

この例では、 という名前labelLabel は、そのプロパティを TextColor に設定しますRed。 プロパティを設定するときは、 TargetNamePropertyプロパティへの完全なパスを指定する必要があります。 したがって、 で LabelProperty プロパティをTextColor設定するには、 としてLabel.TextColor指定します。

注意

オブジェクトによって参照されるすべてのプロパティは、 Setter バインド可能なプロパティによってサポートされている必要があります。

VsmDemos サンプルの VSM with Setter TargetName ページは、1 つのビジュアル状態グループから複数の要素に状態を設定する方法を示しています。 XAML ファイルは、 StackLayout 要素、、 EntryButtonLabel含む で構成されます。

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmSetterTargetNamePage"
             Title="VSM with Setter TargetName">
    <StackLayout Margin="10">
        <Label Text="What is the capital of France?" />
        <Entry x:Name="entry"
               Placeholder="Enter answer" />
        <Button Text="Reveal answer">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
                    <VisualState x:Name="Pressed">
                        <VisualState.Setters>
                            <Setter Property="Scale"
                                    Value="0.8" />
                            <Setter TargetName="entry"
                                    Property="Entry.Text"
                                    Value="Paris" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </Button>
    </StackLayout>
</ContentPage>

VSM マークアップは に StackLayoutアタッチされます。 "Normal" と "Pressed" という名前の相互に排他的な状態が 2 つあり、各状態にはタグが VisualState 含まれています。

が押されていない場合 Button 、"Normal" 状態はアクティブであり、質問への応答を入力できます。

VSM Setter TargetName: Normal State

が押されると Button 、"Pressed" 状態がアクティブになります。

VSM Setter TargetName: Pressed State

"Pressed" VisualState は、 が押されると Button 、その Scale プロパティが既定値の 1 から 0.8 に変更されることを指定します。 さらに、 という名前entryEntry プロパティは Text Paris に設定されます。 したがって、結果として、 が押されると Button 、少し小さいサイズに再スケーリングされ、パリが Entry 表示されます。 次に Button 、 が解放されると、既定値の 1 に再スケーリングされ Entry 、以前に入力したテキストが表示されます。

重要

プロパティ パスは、現在、 プロパティを指定TargetNameする要素ではSetterサポートされていません。

独自のビジュアル状態を定義する

から VisualElement 派生するすべてのクラスは、共通の状態 "Normal"、"Focused"、および "Disabled" をサポートしています。 さらに、 クラスは CollectionView "Selected" 状態をサポートしています。 内部的には、 クラスは VisualElement 、有効または無効になっているか、フォーカスが設定されているかフォーカスされていない状態になるかを検出し、静的 VisualStateManager.GoToState メソッドを呼び出します。

VisualStateManager.GoToState(this, "Focused");

これは、 クラスで VisualElement 見つかる唯一の Visual State Manager コードです。 は からVisualElement派生するすべてのクラスに基づいてすべてのオブジェクトに対して呼び出されるためGoToState、Visual State Manager を任意VisualElementのオブジェクトと共に使用して、これらの変更に応答できます。

興味深いことに、ビジュアル状態グループ "CommonStates" の名前は、 で VisualElement明示的に参照されていません。 グループ名は、Visual State Manager の API の一部ではありません。 これまでに示した 2 つのサンプル プログラムの 1 つ内で、グループの名前を "CommonStates" から他の何かに変更しても、プログラムは引き続き機能します。 グループ名は、そのグループ内の状態の一般的な説明にすぎません。 任意のグループ内のビジュアル状態が相互に排他的であることが暗黙的に理解されます。1 つの状態と、常に 1 つの状態のみが最新です。

独自のビジュアル状態を実装する場合は、コードから を呼び出す VisualStateManager.GoToState 必要があります。 ほとんどの場合、ページ クラスの分離コード ファイルからこの呼び出しを行います。

VsmDemos サンプルの VSM 検証ページは、入力検証に関連して Visual State Manager を使用する方法を示しています。 XAML ファイルは、 StackLayout と の 2 つの Label 要素を Entry含む で 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 x:Name="stackLayout"
                 Padding="10, 10">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidityStates">
                    <VisualState Name="Valid">
                        <VisualState.Setters>
                            <Setter TargetName="helpLabel"
                                    Property="Label.TextColor"
                                    Value="Transparent" />
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Lime" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Invalid">
                        <VisualState.Setters>
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Pink" />
                            <Setter TargetName="submitButton"
                                    Property="Button.IsEnabled"
                                    Value="False" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        <Label Text="Enter a U.S. phone number:"
               FontSize="Large" />
        <Entry x:Name="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" />
        <Button x:Name="submitButton"
                Text="Submit"
                FontSize="Large"
                Margin="0, 20"
                VerticalOptions="Center"
                HorizontalOptions="Center" />
    </StackLayout>
</ContentPage>

VSM マークアップは、 (という名前stackLayout) にStackLayoutアタッチされます。 "Valid" と "Invalid" という名前の相互に排他的な状態が 2 つあり、各状態にはタグが VisualState 含まれています。

Entry 有効な電話番号が含まれていない場合、現在の状態は "無効" であるため Entry 、 はピンク色の背景を持ち、2 つ目 Label は表示され Button 、 は無効になります。

VSM 検証: 無効な状態

有効な電話番号を入力すると、現在の状態は "有効" になります。 は Entry ライムの背景を取得し、2 つ目 Label は消え Button 、 が有効になりました。

VSM 検証: 有効な状態

分離コード ファイルは、 からのイベントを TextChanged 処理します Entry。 ハンドラーは正規表現を使用して、入力文字列が有効かどうかを判断します。 という名前 GoToState の分離コード ファイル内の メソッドは、 の静的 VisualStateManager.GoToState メソッドを stackLayout呼び出します。

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(stackLayout, visualState);
    }
}

状態を GoToState 初期化するために、 メソッドがコンストラクターから呼び出されていることにも注意してください。 常に現在の状態が存在する必要があります。 ただし、わかりやすくするために XAML で "ValidationStates" として参照されていますが、コード内のどこにもビジュアル状態グループの名前への参照はありません。

分離コード ファイルでは、表示状態を定義するページ上の オブジェクトのみを考慮し、このオブジェクトを呼び出す VisualStateManager.GoToState 必要があることに注意してください。 これは、両方のビジュアル状態がページ上の複数のオブジェクトを対象としているためです。

疑問に思うかもしれません。分離コード ファイルが、視覚的な状態を定義するページ上のオブジェクトを参照する必要がある場合、分離コード ファイルでこれと他のオブジェクトに直接アクセスできないのはなぜですか? それは確かにできる。 ただし、VSM を使用する利点は、ビジュアル要素が XAML で異なる状態に完全に反応する方法を制御できることです。これにより、すべての UI デザインが 1 つの場所に保持されます。 これにより、分離コードからビジュアル要素に直接アクセスして、視覚的な外観を設定できなくなります。

ビジュアル状態トリガー

ビジュアル状態では、状態トリガーがサポートされます。これは、 を適用する条件を定義する VisualState 特殊なトリガーのグループです。

状態トリガーは、VisualStateStateTriggers コレクションに追加されます。 このコレクションには、1 つの状態トリガーを含めることも、複数の状態トリガーを含めることもできます。 コレクション内のいずれかの状態トリガーがアクティブになっていると、VisualState が適用されます。

状態トリガーを使用してビジュアルの状態を制御する場合、Xamarin.Forms では、アクティブにするトリガー (および対応する VisualState) を決定するために、次の優先順位規則が使用されます。

  1. StateTriggerBase から派生したトリガー。
  2. MinWindowWidth 条件の適用によってアクティブにされた AdaptiveTrigger
  3. MinWindowHeight 条件の適用によってアクティブにされた AdaptiveTrigger

複数のトリガーが同時にアクティブにされた場合 (たとえば、2 つのカスタム トリガー)、マークアップで最初に宣言されたトリガーが優先されます。

状態トリガーの詳細については、「状態トリガー」を参照してください。

アダプティブ レイアウトに Visual State Manager を使用する

Xamarin.Forms通常、電話で実行されているアプリケーションは縦または横の縦横比で表示でき、Xamarin.Formsデスクトップで実行されているプログラムのサイズを変更して、さまざまなサイズと縦横比を想定できます。 適切に設計されたアプリケーションでは、これらのさまざまなページフォームファクターまたはウィンドウフォームファクターに対してコンテンツが異なる方法で表示される場合があります。

この手法は 、アダプティブ レイアウトと呼ばれることもあります。 アダプティブ レイアウトにはプログラムのビジュアルのみが含まれるため、Visual State Manager の理想的なアプリケーションです。

簡単な例は、アプリケーションのコンテンツに影響を与えるボタンの小さなコレクションを表示するアプリケーションです。 縦モードでは、これらのボタンはページの上部の水平行に表示される場合があります。

VSM アダプティブ レイアウト: 縦向き

横モードでは、ボタンの配列が一方の側に移動され、列に表示されることがあります。

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

上から下へ、プログラムはユニバーサル Windows プラットフォーム、Android、iOS で実行されています。

VsmDemos サンプルの VSM アダプティブ レイアウト ページでは、"OrientationStates" という名前のグループが定義され、"Portrait" と "Landscape" という名前の 2 つのビジュアル状態が定義されています。 (より複雑なアプローチは、複数の異なるページまたはウィンドウの幅に基づいている場合があります)。

VSM マークアップは、XAML ファイル内の 4 つの場所で発生します。 名前付き mainStack にはStackLayout、メニューとコンテンツ (要素) の両方がImage含まれています。 縦 StackLayout モードでは垂直方向、横向きモードでは水平方向である必要があります。

<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内部の 名前付き menuScroll と 名前付き menuStackStackLayout、ボタンのメニューを実装します。 これらのレイアウトの向きは、 の反対です mainStack。 縦モードではメニューは水平、横モードでは垂直にする必要があります。

VSM マークアップの 4 番目のセクションは、ボタン自体の暗黙的なスタイルです。 このマークアップは、縦向きとMargin横向きに固有の 、HorizontalOptions、および プロパティを設定VerticalOptionsします。

分離コード ファイルは、 の プロパティmenuStackBindingContext設定してコマンドを実装Buttonし、ページの イベントにもSizeChangedハンドラーをアタッチします。

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 つの StackLayout 要素と ScrollView 要素を呼び出VisualStateManager.GoToStateし、 のmenuStack子をループ処理して 要素をButton呼び出VisualStateManager.GoToStateします。

分離コード ファイルで XAML ファイル内の要素のプロパティを設定することで方向の変更をより直接処理できるように見えるかもしれませんが、Visual State Manager は間違いなくより構造化されたアプローチです。 すべてのビジュアルは XAML ファイルに保持され、検査、保守、変更が容易になります。

Xamarin.University を使用した Visual State Manager

Xamarin.Forms 3.0 Visual State Manager のビデオ