リスト系コントロールで偶数列で折り返したい

やりたいこと

複数項目を扱うコレクション/リストを扱うコントールでは、幅に応じて自動で折り返してくれる。その折返しを制御したい。

具体的には、必ず偶数列で折り返したい。

例:2列の次は4列になって欲しい:

f:id:kawaishi2:20200720123726p:plain

対象/環境

  • UWP
  • コントロール:GridView, ListView, ItemsRepeater 等
  • Visual Studo 2019

1. Behaviorを使って SizeChangedを監視+GridView

下記サイトを参照 UWP の GridView でいい感じにコンテンツを配置したい@みかづきメモ https://mikazuki.hatenablog.jp/entry/2016/06/26/224545

ほぼ、上記サイトの通りです。

「偶数で折返し」箇所の為に、1行だけ修正した。

private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
    var size = e.NewSize;
    var assumSize = AssumSize.GetAssumSize(this.AssociatedObject);

    var maxColumn = Math.Floor(size.Width / assumSize);

    // ↓ここだけ変更(他一緒)。最大列数を偶数に寄せてる。(3列なら2列、15列なら14列にしてしまう)
    maxColumn = (maxColumn % 2 == 0) ? maxColumn : maxColumn - 1;

    var adjustedSize = assumSize + size.Width % assumSize / maxColumn;

    this.AssociatedObject.ItemHeight = adjustedSize;
    this.AssociatedObject.ItemWidth = adjustedSize;
}

2020/07/21:追記 これ追加すれば列数指定できるのか・・・。

this.AssociatedObject.MaximumRowsOrColumns = (int)maxColumn;

2. ItemsRepeater(WinUI2.x)で、SizeChangedを監視。ItemsRepeater.LayoutのMaximumRowsOrColumns値を制御

気持ち的には これが一番スッキリした。

  • SizeChanged時に処理開始
  • サイズによって、MaximumRowsOrColumnsを偶数に寄せる

1と似てるが、設定するのが「列数」なのがいい。「偶数にしたい!」という意図に近い。

<muxc:ItemsRepeater
         x:Name="repeater2"
         ItemsSource="{x:Bind Path=Vm.MySource}"
         SizeChanged="repeater2_SizeChanged">

         <muxc:ItemsRepeater.Layout>
             <muxc:UniformGridLayout
                x:Name="unilayout"
                MinItemWidth="150"/>
         </muxc:ItemsRepeater.Layout>

</muxc:ItemsRepeater>

private void repeater2_SizeChanged(object sender, Windows.UI.Xaml.SizeChangedEventArgs e)
{
    var iRepeater = sender as ItemsRepeater;
    if(iRepeater == null)
    {
        return;
    }

    // 列幅は150固定とする。2列(300)入る個数を計算し2倍する。偶数のみになるハズ。
     var col = (int)(iRepeater.ActualWidth / 300) * 2;
     this.unilayout.MaximumRowsOrColumns = col;
}

・・・が、 ItemsRepeater はDragDropに(まだ)未対応だった。

ItemsRepeater を GridView にしたらうまく動かなくなった。(あきらめた)

MaximumRowsOrColumns は ItemsRepeater以外でもでも使える・・・ハズ。

なので、2のGridView版はリトライしたい。

> 2020/07/21:追記 できた・・・。

3. ItemsRepeater(WinUI2.x)で、ItemsRepeater.Layout 部分を自作。

やっていることは1と同じ。

こんな感じのことができるらしいので、踏襲しようとした。

Xaml Control Gallery より

f:id:kawaishi2:20200720123743p:plain

<ScrollViewer x:Name="scrollViewer">
    <muxc:ItemsRepeater
            ItemsSource="{x:Bind NumberedItems}"
            Layout="{StaticResource MyFeedLayout}"
            ItemTemplate="{StaticResource SimpleItemTemplate}" />
<ScrollViewer/>

↑ こんな感じで、Layoutを指定する。Layoutは下記。(ItemTemplate とは異なる)

<common:ActivityFeedLayout x:Key="MyFeedLayout" ColumnSpacing="12"
                          RowSpacing="12" MinItemSize="80, 108"/>

↑ common:ActivityFeedLayout というのが自作クラス。

↓ ActivityFeedLayout のソース

ActivityFeedLayout ソース

MeasureOverrideとか、.Netっぽい。この辺を修正して偶数列になるようアイテムの「幅」を調整する。

desiredItemWidth(アイテムの幅) を調整する。奇数になりそうだったら幅を大きくして奇数になるのを回避、偶数になりそうだったら幅を小さくして表示。(という小細工)

ソース省略。

できた・・・けど、自分で書いた内容を忘れそう・・・。(ので採用見送り)

補足

  • 列幅/列数 制御すること自体を見直した方がいいかも?
  • 今は「1」の方法だけど、やっぱり「2をGridView」でやってみたい。

2020/07/21:追記 「2をGridView」 できました。簡単でした・・・。

以上