前提条件
- Microsoft.NETCore.UniversalWindowsPlatform 6.2.14
- Microsoft.UI.Xaml 2.8.5
- ターゲットバージョン Windows 11 ビルド 22000
- 最小バージョン Windows 10, version 2004 ビルド 19041
- Visual Studio 17.7.5
問題
- ScrollViewer + ItemsRepeaterで構成したリスト表示の末尾部分にスクロールしようとした時に
- 意図せず「スクロールが少し戻る → マウススクロールで進もうとする → スクロールが少し戻る」と動作し
- 末尾にスクロールすることが困難になる
以下、この症状を「スクロールが震える」などと表現します。
原因の推測
- ItemsRepeaterに指定される StackLayout などは仮想化をサポートしています。
- 仮想化は、表示範囲にある項目のみをインスタンス化し、表示範囲外に行った要素を使い回すことで少ないリソースで大きなリスト構造を表示することができます。
- 要素をインスタンス化する範囲は HorizontalCacheLength 等で管理され、例えば2を指定した場合にはビューポートの2倍分、現在表示している分+200%の範囲をバッファします。
これらと実際に見た動作を勘案してスクロールが震える症状の原因を推測すると……
- マウススクロールによってバッファ表示区間から要素が外れて消される
- 消された要素の幅に応じてスクロール位置が戻される
- スクロール位置が戻ることで消した要素が再びバッファ表示区間内に収まってしまい再表示される(この時スクロール位置は変わらず)
- これらが合わさって末尾にマウススクロール出来ない症状やスクロールが震える症状となって現れる
対処法1:バッファ表示区間を拡張する
HorizontalCacheLength="3"とするなどバッファ表示区間を大きくすることで震える症状が現れる確率を低減できました。過大に指定すると仮想化による恩恵は受けられなくなるので注意が必要です。
最適?なLengthは、要素幅の最大サイズとItemRepeaterの表示領域に依存します。
例えば幅の大きな要素がバッファ表示区間から消えた場合、その幅の分だけスクロールが戻るため(震える症状は出ませんが)末尾にスクロールを合わせられない症状は変わらず現れます。
対処法2:グループ化せず子アイテムのみで構成する(ネストしている場合)
ネストしたItemsRepeaterを用いて構成することで震える症状が出ている場合は、グループ化解除を検討します。
グループのヘッダーやフッターは子アイテムのグループ内の位置で表示・非表示を判定するようにして子アイテムのみで賄えるようにします。
対処法3:ListViewなどに置き換える
ItemsRepeaterの利用を回避することで問題も回避できます。
対処法4:VirtualizingLayoutをカスタムしてサイズ計算をマニュアルで行う(未検証)