Open3

[.NET MAUI] 入力用リスト(グリッド型)

GomitaGomita

レンタカー予約画面の「乗車地」「降車地」欄で都道府県を入力できるようにしたい。
通常のListViewやCollectionViewだと入力項目が縦方向にスタックされるため、選択肢が多いと縦長になる傾向。都道府県の入力リストはグリッド型(格子状)にしたい。
MAUIではFlexLayoutにより、子の要素を水平方向にスタックして溢れた場合はラップさせるレイアウトが可能だが、子の要素を動的に生成するListViewやCollectionView内でFlexLayoutを使うことはできなそう。
どうしてもListViewで水平方向にスタックしたい場合、この辺りのサードパーティのSDK(有料)を使用する必要がありそう?
https://docs.telerik.com/devtools/maui/knowledge-base/listview-horizontal-orientation--gridlayout
https://help.syncfusion.com/maui/listview/getting-started

GomitaGomita

今回はListViewは諦めて、C#で動的にButtonを生成してFlexLayoutの子に追加する方法で実装してみた。まずはアプリにバンドルしたファイルの読み込みと同じ方法で都道府県のデータregion.jsonをアプリにバンドルする。

/Resources/Raw/region.json
[
  { "Code": 1, "Name": "北海道" },
  { "Code": 2, "Name": "青森県" },
  { "Code": 3, "Name": "岩手県" },
  ...
  { "Code": 47, "Name": "沖縄県" }
]

XAML側に都道府県入力用リストを追加する。中身は空っぽのFlexLayoutのみ。Wrap属性によりコードビハインド側で追加する子要素を折り返しでレイアウトできるようにする。

MainPage.xaml
+        <!-- ▼都道府県入力用リスト -->
+        <ScrollView x:Name="RegionList"
+                  AbsoluteLayout.LayoutFlags="PositionProportional"
+                  AbsoluteLayout.LayoutBounds="0.5,1,375,300">
+            <FlexLayout x:Name="RegionListFlexLayout" Wrap="Wrap" />
+        </ScrollView>

コードビハインド側ではregion.jsonを読み込んで、RegionクラスのListコレクションにパースし、Listをforeachで回しながらButtonを動的に生成してFlexLayoutの子要素として追加していく。

MainPage.xaml.cs
    private async void MainPage_Loaded(object sender, EventArgs e)
    {
        ...
+        // 都道府県リストの生成
+        filePath = NSBundle.MainBundle.ResourcePath;
+        filePath = Path.Combine(filePath, "Raw");
+        filePath = Path.Combine(filePath, "region.json");
+        json = File.ReadAllText(filePath);
+        var rlst = JsonSerializer.Deserialize<List<Region>>(json);
+        foreach (Region r in rlst)
+        {
+            RegionListFlexLayout.Children.Add(
+                new Button { Text = r.Name }
+            );
+        }
         // リストを非表示にする
         CarTypeList.IsVisible = false;
+        RegionList.IsVisible = false;
    }
    ...
+public class Region
+{
+    public int Code { get; set; } = 0;
+    public string Name { get; set; } = "";
+}

「乗車地」「降車地」入力欄にフォーカスしたときのイベント処理などは省略するが、無事にグリッド上にボタンが並ぶリストが表示できるようになった。

GomitaGomita

標準のButtonコントロールだと見た目がイマイチなので、かっこよくする。

MAUIではCSSもサポートされており、複数のコントロールの見た目を一括で設定することが可能。
https://learn.microsoft.com/ja-jp/dotnet/maui/user-interface/styles/css?view=net-maui-8.0
最初、MainPage.xamlでCDATAセクションにより#RegionList Buttonに対するCSSを指定する方法を試したが、Buttonに対するbackground-colorがサポートされていない(?)ようで断念した。

次に、XAMLのStyleクラスを使用する方法を試した。
https://learn.microsoft.com/ja-jp/dotnet/maui/user-interface/styles/xaml?view=net-maui-8.0
<Style x:key="hoge"...で設定したスタイルをコードビハインド側で動的に生成したButtonだけに適用させる方法がわからず(当て勘でButton.StyleClass.Add("hoge")など試したがダメ)。

最終的に、以下のように<ScrollView.Resources>をScrollViewの中に突っ込むことで解決。

MainPage.xaml
        <!-- ▼都道府県入力用リスト -->
        <ScrollView x:Name="RegionList"
                  AbsoluteLayout.LayoutFlags="PositionProportional"
                  AbsoluteLayout.LayoutBounds="0.5,1,375,300">
+            <ScrollView.Resources>
+                <Style TargetType="Button">
+                    <Setter Property="FontSize" Value="16" />
+                    <Setter Property="BackgroundColor" Value="White" />
+                    <Setter Property="TextColor" Value="Black" />
+                    <Setter Property="BorderColor" Value="LightGray" />
+                    <Setter Property="BorderWidth" Value="1" />
+                    <Setter Property="WidthRequest" Value="75" />
+                    <Setter Property="HeightRequest" Value="85" />
+                </Style>
+            </ScrollView.Resources>
            <FlexLayout x:Name="RegionListFlexLayout" Wrap="Wrap" />
        </ScrollView>

無事、都道府県リストが綺麗な格子状のボタンで表示された!