XAML でのレスポンシブ レイアウト

XAML レイアウト システムでは、応答性の高い UI を作成するために、要素の自動サイズ変更、レイアウト パネル、表示状態が提供されます。 レスポンシブ レイアウトを使用すると、アプリのウィンドウ サイズ、解像度、ピクセル密度、向きが異なる画面でアプリを見栄えさせることができます。 XAML を使用して、アプリの UI の位置変更、サイズ変更、リフロー、表示/非表示、置換、または再設計を行うこともできます( 応答設計手法で説明します。 ここでは、XAML を使用してレスポンシブ レイアウトを実装する方法について説明します。

プロパティとパネルを含む柔軟なレイアウト

レスポンシブ レイアウトの基礎は、XAML レイアウトのプロパティとパネルを適切に使用して、コンテンツの位置変更、サイズ変更、リフローを柔軟に行うことです。

XAML レイアウト システムでは、静的レイアウトと滑らかなレイアウトの両方がサポートされています。 静的レイアウトでは、コントロールに明示的なピクセル サイズと位置を指定します。 ユーザーがデバイスの解像度または向きを変更しても、UI は変更されません。 静的レイアウトは、さまざまなフォーム ファクターと表示サイズにまたがってクリップされる可能性があります。 一方、柔軟なレイアウトは縮小、拡大、およびリフローされ、デバイスで使用可能な視覚空間に対応します。

実際には、静的要素と流体要素の組み合わせを使用して UI を作成します。 場所によっては静的な要素と値を使うこともありますが、UI 全体のさまざまな解像度、画面サイズ、ビューへの応答性を高くする必要があります。

ここでは、XAML プロパティとレイアウト パネルを使用して柔軟なレイアウトを作成する方法について説明します。

レイアウト プロパティ

レイアウト プロパティは、要素のサイズと位置を制御します。 柔軟なレイアウトを作成するには、要素の自動または比例サイズ設定を使用し、必要に応じて、レイアウト パネルで子を配置できるようにします。

一般的なレイアウト プロパティと、それらを使用して流動的なレイアウトを作成する方法を次に示します。

Height および Width

Heightプロパティと Width プロパティは、要素のサイズを指定します。 有効ピクセル単位で測定された固定値を使用することも、自動または比例サイズ設定を使用することもできます。

自動サイズ設定では、UI 要素のサイズがコンテンツまたは親コンテナーに合わせて変更されます。 グリッドの行と列で自動サイズ設定を使用することもできます。 自動サイズ設定を使用するには、UI 要素の高さまたは幅を Auto に設定します。

Note

要素のサイズをコンテンツに変更するか、そのコンテナーに変更するかは、親コンテナーが子のサイズ変更をどのように処理するかによって異なります。 詳細については、この記事で後述 レイアウト パネル を参照してください。

プロポーショナル サイズ設定 ( star sizing とも呼ばれます) は、グリッドの行と列の間に、加重比率で使用可能な領域を分散します。 XAML では、スター値は * (重み付きのスター サイズ指定の場合は n*) として表されます。 たとえば、2 段組レイアウトで 1 列目が 2 列目の 5 倍の幅であることを指定するには、ColumnDefinition 要素の Width プロパティで "5*" と "*" を使います。

次の使用例は、 Grid で固定、自動、比例サイズ変更を組み合わせ 4 列を使用します。

サイズ変更 説明
Column_1 Auto 列のサイズは、その内容に合わせて調整されます。
Column_2 * 自動列が計算されると、列は残りの幅の一部を取得します。 Column_2は、Column_4の半分の幅になります。
Column_3 44 列の幅は 44 ピクセルです。
Column_4 2* 自動列が計算されると、列は残りの幅の一部を取得します。 Column_4はColumn_2の 2 倍の幅になります。

既定の列幅は "*" であるため、2 番目の列に対してこの値を明示的に設定する必要はありません。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
        <ColumnDefinition Width="44"/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Column 1 sizes to its content." FontSize="24"/>
</Grid>

Visual Studio XAML デザイナーでは、結果は次のようになります。

Visual Studio デザイナーの 4 列のグリッド

実行時に要素のサイズを取得するには、Height プロパティと Width プロパティではなく、読み取り専用の ActualHeight プロパティと ActualWidth プロパティを使用します。

サイズの制約

UI で自動サイズ設定を使用する場合でも、要素のサイズに制約を設定する必要がある場合があります。 MinWidth/MaxWidth プロパティと MinHeight/MaxHeight プロパティを設定して、柔軟なサイズ変更を許可しながら要素のサイズを制限する値を指定できます。

グリッドでは、MinWidth/MaxWidth を列定義と共に使用することもでき、MinHeight/MaxHeight は行定義と共に使用できます。

位置合わせ

HorizontalAlignmentプロパティと VerticalAlignment プロパティを使用して、親コンテナー内での要素の配置方法を指定します。

  • HorizontalAlignment の値は、LeftCenterRight、および Stretch です。
  • VerticalAlignment の値は、TopCenterBottom、および Stretch です。

Stretch配置では、要素は親コンテナーに用意されているすべてのスペースを埋めます。 ストレッチは、両方の配置プロパティの既定値です。 ただし、 Button などの一部のコントロールは、既定のスタイルでこの値をオーバーライドします。 子要素を持つ要素は、HorizontalAlignment プロパティと VerticalAlignment プロパティの Stretch 値を一意に処理できます。 たとえば、Grid に配置された既定の Stretch 値を使用する要素は、それを含むセルを塗りつぶすために引き伸ばされます。 キャンバスに配置された同じ要素は、そのコンテンツに合わせてサイズを設定します。 各パネルでの Stretch 値の処理方法の詳細については、 レイアウト パネル に関する記事を参照してください。

詳細については、 Alignment、margin、padding に関する記事、および HorizontalAlignment および VerticalAlignment リファレンス ページを参照してください。

表示

要素の Visibility プロパティを Visibility 列挙 値 ( Visible または Collapsed のいずれかに設定すると、表示または非表示にすることができます。 要素が折りたたまれている場合、UI レイアウトの領域は占有されません。

要素の Visibility プロパティは、コードまたは表示状態で変更できます。 要素の可視性が変更されると、そのすべての子要素も変更されます。 1 つのパネルを表示しながら別のパネルを折りたたむことで、UI のセクションを置き換えることができます。

ヒント

UI に既定で Collapsed である要素がある場合、要素が表示されていなくても、オブジェクトは起動時に作成されます。 x:Load 属性を使用して表示されるまで、これらの要素の読み込みを延期してオブジェクトの作成を遅らせることができます。 これにより、スタートアップ のパフォーマンスが向上します。 詳細については、「 x:Load attribute」を参照してください。

Style リソース

コントロールで各プロパティ値を個別に設定する必要はありません。 通常、プロパティ値を Style リソースにグループ化し、Style をコントロールに適用する方が効率的です。 これは、多くのコントロールに同じプロパティ値を適用する必要がある場合に特に当てはまります。 スタイルの使用方法の詳細については、「 Styling コントロール」を参照してください。

レイアウト パネル

ビジュアル オブジェクトを配置するには、それらをパネルまたはその他のコンテナー オブジェクトに配置する必要があります。 XAML フレームワークには、 CanvasGridRelativePanelStackPanel など、さまざまなパネル クラスが用意されています。これにより、コンテナーとして機能し、UI 要素を配置して配置できます。

レイアウト パネルを選択する際に考慮すべき主な点は、パネルの子要素の位置とサイズです。 また、重なり合う子要素が重なり合う方法を考慮する必要がある場合もあります。

XAML フレームワークで提供されるパネル コントロールの主な機能の比較を次に示します。

パネル コントロール 説明
Canvas Canvas は流動的 UI をサポートしていません。子要素の配置とサイズ設定のすべての側面を制御します。 通常は、グラフィックスの作成や、より大きなアダプティブ UI の小さな静的領域の定義などの特殊な場合に使用します。 コードまたはビジュアルの状態を使用して、実行時に要素の位置を変更できます。
  • 要素は、Canvas.Top および Canvas.Left 添付プロパティを使用して絶対に配置されます。
  • レイヤーは Canvas.ZIndex 添付プロパティを使用して明示的に指定できます。
  • HorizontalAlignment/VerticalAlignment のストレッチ値は無視されます。 要素のサイズが明示的に設定されていない場合は、そのコンテンツに合わせてサイズが設定されます。
  • パネルよりも大きい場合、子コンテンツは視覚的にクリップされません。
  • 子コンテンツは、パネルの境界によって制限されません。
  • Grid Grid では、子要素の柔軟なサイズ変更がサポートされます。 コードまたはビジュアルの状態を使用して、要素の位置を変更およびリフローできます。
  • 要素は、Grid.Row および Grid.Column 添付プロパティを使用して行と列に配置されます。
  • 要素は、Grid.RowSpan および Grid.ColumnSpan 添付プロパティを使用して、複数の行と列にまたがることができます。
  • HorizontalAlignment/VerticalAlignment のストレッチ値が考慮されます。 要素のサイズが明示的に設定されていない場合は、グリッド セル内の使用可能な領域を埋めるように拡張されます。
  • パネルより大きい場合、子コンテンツは視覚的にクリップされます。
  • コンテンツ サイズはパネルの境界によって制限されるため、スクロール可能なコンテンツには必要に応じてスクロール バーが表示されます。
  • RelativePanel
  • 要素は、パネルの端または中心に対して、および相互に関連して配置されます。
  • 要素は、コントロール パネルの配置、兄弟の配置、および兄弟の位置を制御するさまざまな添付プロパティを使用して配置されます。
  • HorizontalAlignment/VerticalAlignment のストレッチ値は、配置の RelativePanel 添付プロパティによってストレッチが発生しない限り無視されます (たとえば、要素がパネルの右端と左端の両方に配置されます)。 要素のサイズが明示的に設定されておらず、拡張されていない場合は、そのコンテンツに合わせてサイズが変更されます。
  • パネルより大きい場合、子コンテンツは視覚的にクリップされます。
  • コンテンツ サイズはパネルの境界によって制限されるため、スクロール可能なコンテンツには必要に応じてスクロール バーが表示されます。
  • StackPanel
  • 要素は、垂直方向または水平方向に 1 行に積み上げされます。
  • HorizontalAlignment/VerticalAlignment のストレッチ値は、Orientation プロパティの反対方向で考慮されます。 要素のサイズが明示的に設定されていない場合は、使用可能な幅 (または Orientation が Horizontal の場合は高さ) に合わせて拡張されます。 Orientation プロパティで指定された方向で、要素はそのコンテンツに合わせてサイズを変更します。
  • パネルより大きい場合、子コンテンツは視覚的にクリップされます。
  • コンテンツ サイズは、Orientation プロパティで指定された方向のパネルの境界によって制限されないため、スクロール可能なコンテンツはパネルの境界を超えて拡大され、スクロール バーは表示されません。 スクロール バーを表示するには、子コンテンツの高さ (または幅) を明示的に制限する必要があります。
  • VariableSizedWrapGrid
  • MaximumRowsOrColumns 値に達すると、新しい行または列に自動的に折り返される行または列に要素が配置されます。
  • 要素を行または列に配置するかどうかは、Orientation プロパティで指定します。
  • 要素は、VariableSizedWrapGrid.RowSpan および VariableSizedWrapGrid.ColumnSpan 添付プロパティを使用して、複数の行と列にまたがることができます。
  • HorizontalAlignment と VerticalAlignment のストレッチ値は無視されます。 要素のサイズは、ItemHeight プロパティと ItemWidth プロパティで指定されたサイズに設定されます。 これらのプロパティが設定されていない場合は、最初のセルのサイズから値が取得されます。
  • パネルより大きい場合、子コンテンツは視覚的にクリップされます。
  • コンテンツ サイズはパネルの境界によって制限されるため、スクロール可能なコンテンツには必要に応じてスクロール バーが表示されます。
  • これらのパネルの詳細と例については、「 レイアウト パネルを参照してください。

    レイアウト パネルを使用すると、UI をコントロールの論理グループに整理できます。 適切なプロパティ設定で使用すると、UI 要素の自動サイズ変更、再配置、リフローがサポートされます。 ただし、ほとんどの UI レイアウトでは、ウィンドウ サイズに大きな変更がある場合は、さらに変更する必要があります。 このためには、表示状態を使用できます。

    表示状態と状態トリガーを使用したアダプティブ レイアウト

    表示状態を使用して、ウィンドウ サイズやその他の変更に基づいて UI を大幅に変更します。

    アプリ ウィンドウが一定の量を超えて拡大または縮小する場合は、レイアウト プロパティを変更して、UI のセクションの位置変更、サイズ変更、リフロー、表示、または置換を行う必要がある場合があります。 UI に異なる表示状態を定義し、ウィンドウの幅またはウィンドウの高さが指定したしきい値を超えたときにそれらを適用できます。

    VisualStateは、特定の状態のときに要素に適用されるプロパティ値を定義します。 指定した条件が満たされたときに適切な VisualState を適用する VisualStateManager で表示状態をグループ化します。 AdaptiveTrigger を使うと、XAML に状態を適用するしきい値 ("ブレークポイント" とも呼ばれます) を簡単に設定できます。 または、コードで VisualStateManager.GoToState メソッドを呼び出して表示状態を適用することもできます。 次のセクションで、両方の方法の例を示します。

    コードで表示状態を設定する

    コードからビジュアル状態を適用するには、 VisualStateManager.GoToState メソッドを呼び出します。 たとえば、アプリ ウィンドウが特定のサイズのときに状態を適用するには、 SizeChanged イベントを処理し、 GoToState を呼び出して適切な状態を適用します。

    ここでは、 VisualStateGroup には 2 つの VisualState 定義が含まれています。 最初の DefaultStateは空です。 適用されると、XAML ページで定義されている値が適用されます。 2 つ目のWideStateでは、SplitViewDisplayMode プロパティを Inline に変更し、ウィンドウを開きます。 ウィンドウの幅が有効ピクセル数 640 を超える場合、SizeChanged イベント ハンドラーでこの状態が適用されます。

    Note

    Windows では、アプリが実行されている特定のデバイスをアプリが検出する方法は提供されていません。 アプリが実行されているデバイス ファミリ (デスクトップなど)、有効な解像度、アプリで使用できる画面領域の量 (アプリのウィンドウのサイズ) を示すことができます。 画面のサイズとブレークポイント 表示状態を定義することをお勧めします

    <Page ...
        SizeChanged="CurrentWindow_SizeChanged">
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState x:Name="DefaultState">
                            <Storyboard>
                            </Storyboard>
                        </VisualState>
    
                    <VisualState x:Name="WideState">
                        <Storyboard>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.DisplayMode"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0">
                                    <DiscreteObjectKeyFrame.Value>
                                        <SplitViewDisplayMode>Inline</SplitViewDisplayMode>
                                    </DiscreteObjectKeyFrame.Value>
                                </DiscreteObjectKeyFrame>
                            </ObjectAnimationUsingKeyFrames>
                            <ObjectAnimationUsingKeyFrames
                                Storyboard.TargetProperty="SplitView.IsPaneOpen"
                                Storyboard.TargetName="mySplitView">
                                <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
                            </ObjectAnimationUsingKeyFrames>
                        </Storyboard>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    
    private void CurrentWindow_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
    {
        if (e.Size.Width > 640)
            VisualStateManager.GoToState(this, "WideState", false);
        else
            VisualStateManager.GoToState(this, "DefaultState", false);
    }
    
    // YourPage.h
    void CurrentWindow_SizeChanged(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Xaml::SizeChangedEventArgs const& e);
    
    // YourPage.cpp
    void YourPage::CurrentWindow_SizeChanged(IInspectable const& sender, SizeChangedEventArgs const& e)
    {
        if (e.NewSize.Width > 640)
            VisualStateManager::GoToState(*this, "WideState", false);
        else
            VisualStateManager::GoToState(*this, "DefaultState", false);
    }
    
    

    XAML マークアップで表示状態を設定する

    Windows 10 より前の VisualState 定義では、プロパティの変更に Storyboard オブジェクトが必要でした。状態を適用するには、コードで GoToState を呼び出す必要がありました。 上記の例を参照してください。 この構文を使用する多くの例が表示されます。または、それを使用する既存のコードがある可能性があります。

    Windows 10 以降では、ここで示す簡略化された Setter 構文を使用でき、XAML マークアップで StateTrigger を使用して状態を適用できます。 状態トリガーを使用して、アプリ イベントに応答して視覚的な状態の変更を自動的にトリガーする単純なルールを作成します。

    この例は前の例と同じことを行いますが、ストーリーボードの代わりに簡略化された Setter 構文を使用してプロパティの変更を定義します。 また、GoToState を呼び出す代わりに、組み込みの AdaptiveTrigger 状態トリガーを使用して状態を適用します。 状態トリガーを使用する場合は、空の DefaultStateを定義する必要はありません。 状態トリガーの条件が満たされなくなると、既定の設定が自動的に再適用されます。

    <Page ...>
        <Grid>
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState>
                        <VisualState.StateTriggers>
                            <!-- VisualState to be triggered when the
                                 window width is >=640 effective pixels. -->
                            <AdaptiveTrigger MinWindowWidth="640" />
                        </VisualState.StateTriggers>
    
                        <VisualState.Setters>
                            <Setter Target="mySplitView.DisplayMode" Value="Inline"/>
                            <Setter Target="mySplitView.IsPaneOpen" Value="True"/>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
    
            <SplitView x:Name="mySplitView" DisplayMode="CompactInline"
                       IsPaneOpen="False" CompactPaneLength="20">
                <!-- SplitView content -->
    
                <SplitView.Pane>
                    <!-- Pane content -->
                </SplitView.Pane>
            </SplitView>
        </Grid>
    </Page>
    

    重要

    前の例では、Grid 要素に対して VisualStateManager.VisualStateGroups 添付プロパティが設定されています。 StateTriggers を使用する場合は、トリガーが自動的に有効になるように、常に VisualStateGroups がルートの最初の子にアタッチされていることを確認します。 (こちら、 Grid は、ルート Page 要素の最初の子です。

    添付プロパティの構文

    VisualState では、通常、コントロール プロパティまたはコントロールを含むパネルの添付プロパティの 1 つに値を設定します。 添付プロパティを設定する場合は、添付プロパティ名をかっこで囲みます。

    この例では、 RelativePanel.AlignHorizontalCenterWithPanel 添付プロパティを myTextBox という名前の TextBox に設定する方法を示します。 1 つ目の XAML では ObjectAnimationUsingKeyFrames 構文が使用され、2 つ目の XAML では Setter 構文が使用されます。

    <!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
    <ObjectAnimationUsingKeyFrames
        Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
        Storyboard.TargetName="myTextBox">
        <DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
    </ObjectAnimationUsingKeyFrames>
    
    <!-- Set an attached property using Setter. -->
    <Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
    

    カスタム状態トリガー

    StateTrigger クラスを拡張して、さまざまなシナリオに対応するカスタム トリガーを作成できます。 たとえば、入力の種類に基づいてさまざまな状態をトリガーする StateTrigger を作成し、入力の種類がタッチされたときにコントロールの周囲の余白を増やすことができます。 または、アプリが実行されているデバイス ファミリに基づいてさまざまな状態を適用する StateTrigger を作成します。 カスタム トリガーをビルドし、それらを使用して単一の XAML ビュー内から最適化された UI エクスペリエンスを作成する方法の例については、 State トリガーのサンプルを参照してください。

    表示状態とスタイル

    表示状態で Style リソースを使用して、一連のプロパティ変更を複数のコントロールに適用できます。 スタイルの使用方法の詳細については、「 Styling コントロール」を参照してください。

    State トリガーサンプルのこの簡略化された XAML では、マウスまたはタッチ入力のサイズと余白を調整するために、Style リソースが Button に適用されます。 完全なコードとカスタム状態トリガーの定義については、 State トリガーのサンプルを参照してください。

    <Page ... >
        <Page.Resources>
            <!-- Styles to be used for mouse vs. touch/pen hit targets -->
            <Style x:Key="MouseStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="5" />
                <Setter Property="Height" Value="20" />
                <Setter Property="Width" Value="20" />
            </Style>
            <Style x:Key="TouchPenStyle" TargetType="Rectangle">
                <Setter Property="Margin" Value="15" />
                <Setter Property="Height" Value="40" />
                <Setter Property="Width" Value="40" />
            </Style>
        </Page.Resources>
    
        <RelativePanel>
            <!-- ... -->
            <Button Content="Color Palette Button" x:Name="MenuButton">
                <Button.Flyout>
                    <Flyout Placement="Bottom">
                        <RelativePanel>
                            <Rectangle Name="BlueRect" Fill="Blue"/>
                            <Rectangle Name="GreenRect" Fill="Green" RelativePanel.RightOf="BlueRect" />
                            <!-- ... -->
                        </RelativePanel>
                    </Flyout>
                </Button.Flyout>
            </Button>
            <!-- ... -->
        </RelativePanel>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="InputTypeStates">
                <!-- Second set of VisualStates for building responsive UI optimized for input type.
                     Take a look at InputTypeTrigger.cs class in CustomTriggers folder to see how this is implemented. -->
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- This trigger indicates that this VisualState is to be applied when MenuButton is invoked using a mouse. -->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Mouse" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource MouseStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource MouseStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
                <VisualState>
                    <VisualState.StateTriggers>
                        <!-- Multiple trigger statements can be declared in the following way to imply OR usage.
                             For example, the following statements indicate that this VisualState is to be applied when MenuButton is invoked using Touch OR Pen.-->
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Touch" />
                        <triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Pen" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="BlueRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <Setter Target="GreenRect.Style" Value="{StaticResource TouchPenStyle}" />
                        <!-- ... -->
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Page>