WinUI3アプリでWindowsの設定のホーム画面のようなレスポンシブルっぽい動きをさせる

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

Windowsの設定のホーム画面のように、

  • 設定項目1つ分の枠について、
    • 2列表示の時、
      • ある程度の幅に収まっているときは、ウインドウの幅に合わせて設定項目の枠の幅が伸び縮みする
      • ウインドウが一定の幅より大きくなると、設定項目の枠はそれ以上幅が大きくならない
      • ウインドウが一定の幅より小さくなると、設定項目の枠はそれ以上幅が大きくならない
      • さらにウインドウの幅が小さくなると、1列表示になる
    • 1列表示の時、
      • ある程度の幅に収まっているときは、ウインドウの幅に合わせて設定項目の枠の幅が伸び縮みする
      • ウインドウが一定の幅より大きくなると、2列表示になる

という動きをさせたい。↓のようなイメージ。

※こういうのを「レスポンシブ」というのか?

前提

以下の環境で実験した。

作成したコード

github.com

動かすとこうなる

コード中身

<?xml version="1.0" encoding="utf-8"?>
<Page
    x:Class="MyWinUI3PackageProjectTemplate6.View.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MyWinUI3PackageProjectTemplate6.View"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <ItemsRepeater ItemsSource="{x:Bind MyDatas}" MaxWidth="800">
            <ItemsRepeater.Layout>
                <!-- ItemsStretch を Fill にすると、ウインドウの幅までItemsRepeaterが広がってくれる -->
                <!-- そのうえで、ItemsRepeater自体のMaxWidthを、MinItemWidth の2倍より少し大きくしておくと、2列になったうえで各Itemの大きさが少しだけ大きくなってくれるような動きになる-->
                <UniformGridLayout MaximumRowsOrColumns="2" MinItemWidth="300" MinColumnSpacing="10" MinRowSpacing="10" ItemsStretch="Fill" ItemsJustification="Center"/>
            </ItemsRepeater.Layout>

            <DataTemplate x:DataType="local:MyData">
                <Border BorderBrush="Red" BorderThickness="2" Margin="2" SizeChanged="Border_SizeChanged">
                    <Grid HorizontalAlignment="Stretch" Background="SkyBlue" Name="outer">
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>

                        <TextBlock Grid.Row="0" Text="{x:Bind Data1}" HorizontalAlignment="Center"/>
                        <ContentPresenter Grid.Row="2" Content="{x:Bind Toggle}" HorizontalAlignment="Center"/>
                    </Grid>
                </Border>
            </DataTemplate>
        </ItemsRepeater>

        <!-- デバッグ用 -->
        <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Bottom" >
            <TextBlock x:Name="ItemSize" Foreground="Red"/>
            <Button Content="statusを進める" Click="Button_Click" />
        </StackPanel>
    </Grid>
</Page>

解説

ItemsRepeaterに適用したUniformGridLayoutItemsStretchをFillにしておくと、 ウインドウの幅とItemsRepeaterの大きさが同じになってくれる。 (ウインドウを広げると、Itemの幅も広がってくれる)

UniformGridLayoutのMaximumRowsOrColumnsを2にしておくと、Itemの横並びが最大二個までになる。

ウインドウを縮めて、Itemの1こあたりの幅がMinItemWidthで指定した幅(今回の場合だと300)より小さくなると、2列だったItemが1列になる。

ItemsRepeater自体のMaxWidthを、MinItemWidthの2倍より少し大きくしておくと、2列になったうえで各Itemの大きさが少しだけ大きくなってくれるような動きになる。

ItemsJustificationをいろいろ変えると、Itemの配置をいろいろ変えられる。

参考

XAML でのレスポンシブ レイアウト(直接実装方法には関係ないページ)

https://learn.microsoft.com/ja-jp/windows/apps/design/layout/layouts-with-xaml