Windows UI Library に ItemsRepeater という軽量で仮想化機能がついている ItemsControl のような動きをするクラスがあります。
このクラスはレイアウトをカスタマイズ可能で Windows UI Library 2.1 ではデフォルトで StackLayout と UniformGridLayout が提供されています。
使い方は非常にシンプルで、ItemsRepeater の ItemsSource にコレクションを設定して ItemTemplate で見た目を指定するだけです。スクロール機能が必要な場合は ScrollViewer にラップするだけです。
以下のコードは string の配列の Items プロパティをコードビハインドに定義して表示する XAML になります。
<Page
x:Class="FlowLayoutApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlowLayoutApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ScrollViewer>
<controls:ItemsRepeater ItemsSource="{x:Bind Items}">
<controls:ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="x:String">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Width="30" Height="30" Fill="AliceBlue" Margin="10" />
<TextBlock Text="{x:Bind}" Grid.Column="1" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</controls:ItemsRepeater.ItemTemplate>
</controls:ItemsRepeater>
</ScrollViewer>
</Grid>
</Page>
以下のように表示されます。
Layout に UniformGridLayout を指定すると折り返しコンテンツが表示できます。試しに XAML を以下のように変更します。
<Page
x:Class="FlowLayoutApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlowLayoutApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<ScrollViewer>
<controls:ItemsRepeater ItemsSource="{x:Bind Items}">
<controls:ItemsRepeater.Layout>
<controls:UniformGridLayout MinItemWidth="200" />
</controls:ItemsRepeater.Layout>
<controls:ItemsRepeater.ItemTemplate>
<DataTemplate x:DataType="x:String">
<Border BorderThickness="1" BorderBrush="Beige">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Width="30" Height="30" Fill="AliceBlue" Margin="10" />
<TextBlock Text="{x:Bind}" Grid.Column="1" VerticalAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
</controls:ItemsRepeater.ItemTemplate>
</controls:ItemsRepeater>
</ScrollViewer>
</Grid>
</Page>
実行すると以下のようになります。
ただ、この UniformGridLayout は名前の通りコンテンツサイズが各々可変の場合に問題が起きます。レイアウトが均一になるので、例えば要素ごとにサイズが可変のものは思った通りにレイアウトできません。
例えば、以下のようにデータに "3" が含まれているものと含まれていないものでテンプレートをわける DataTemplateSelector を実装したとします。
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace FlowLayoutApp
{
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate NoThreeTemplate { get; set; }
public DataTemplate IsInThreeTemplate { get; set; }
private DataTemplate Get(string x) => x.Contains("3") ? IsInThreeTemplate : NoThreeTemplate;
protected override DataTemplate SelectTemplateCore(object item) => Get((string)item);
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) => Get((string)item);
}
}
そして、それを UniformGridLayout を設定した ItemsRepeater に適用します。
<Page
x:Class="FlowLayoutApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlowLayoutApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<local:MyTemplateSelector x:Key="myTemplateSelector">
<local:MyTemplateSelector.IsInThreeTemplate>
<DataTemplate x:DataType="x:String">
<Border BorderThickness="1" BorderBrush="Beige">
<Grid Width="250" Height="250">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Rectangle Width="50" Height="50" Fill="Blue" Margin="10" />
<TextBlock Text="{x:Bind}" Grid.Column="1" VerticalAlignment="Center" />
<Rectangle Width="50" Height="50" Fill="Blue" Margin="10" Grid.Column="2" />
</Grid>
</Border>
</DataTemplate>
</local:MyTemplateSelector.IsInThreeTemplate>
<local:MyTemplateSelector.NoThreeTemplate>
<DataTemplate x:DataType="x:String">
<Border BorderThickness="1" BorderBrush="Beige">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Rectangle Width="30" Height="30" Fill="AliceBlue" Margin="10" />
<TextBlock Text="{x:Bind}" Grid.Column="1" VerticalAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
</local:MyTemplateSelector.NoThreeTemplate>
</local:MyTemplateSelector>
</Page.Resources>
<Grid>
<ScrollViewer>
<controls:ItemsRepeater ItemsSource="{x:Bind Items}" ItemTemplate="{StaticResource myTemplateSelector}">
<controls:ItemsRepeater.Layout>
<controls:UniformGridLayout />
</controls:ItemsRepeater.Layout>
</controls:ItemsRepeater>
</ScrollViewer>
</Grid>
</Page>
このようにサイズが可変ではないので、ちゃんと表示されません。
FlowLayout
Windows UI Library の 2.2 (現時点でプレビューリリース)で FlowLayout が追加されます。これを ItemsRepeater で使うと先ほどのようなケースでもいい感じに表示できます。Windows UI Library を 2.2 (プレビュー) にして UniformGridLayout を FlowLayout に変更します。
<Page
x:Class="FlowLayoutApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:FlowLayoutApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<local:MyTemplateSelector x:Key="myTemplateSelector">
<!-- 省略 -->
</local:MyTemplateSelector>
</Page.Resources>
<Grid>
<ScrollViewer>
<controls:ItemsRepeater ItemsSource="{x:Bind Items}" ItemTemplate="{StaticResource myTemplateSelector}">
<controls:ItemsRepeater.Layout>
<controls:FlowLayout />
</controls:ItemsRepeater.Layout>
</controls:ItemsRepeater>
</ScrollViewer>
</Grid>
</Page>
実行すると、以下のようになります。
複数サイズの要素がきちんと表示されていることがわかります。
ただ、まだプレビュー段階の実験的機能の位置づけなのでスクロールをすると表示が崩れたり動作が不安定になります。
まとめ
FlowLayout がちゃんと動くようになると、色々なサイズの可変要素を仮想化が効いた状態で表示することが可能になるはずです。
そうなってくると、結構柔軟な表示が可能になると思うので楽しみですね。