WinUI中如何让ItemsRepeater将可用宽度均分给子项?
解决方案
方案一:ItemsControl + 动态列Grid
通过绑定集合数量生成等宽列的Grid,配合ItemsControl实现动态子项的平分布局,强制子项不换行、挤压适应空间。
步骤1:实现两个转换器
需要两个值转换器支撑动态列逻辑:
- 集合数量转ColumnDefinitions:将集合元素数量转换为对应数量的
ColumnDefinition(每个列宽为*)
public class CountToColumnDefinitionsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is int count && count > 0) { var columns = new ColumnDefinitionCollection(); for (int i = 0; i < count; i++) { columns.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); } return columns; } return new ColumnDefinitionCollection(); } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
- 子项索引转Grid.Column:获取子项在ItemsControl中的索引,设置对应的Grid列位置
public class IndexToGridColumnConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value is ContentPresenter presenter && presenter.TemplatedParent is ItemsControl itemsControl) { return itemsControl.ItemContainerGenerator.IndexFromContainer(presenter); } return 0; } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } }
步骤2:XAML布局
在资源中声明转换器,然后构建ItemsControl布局:
<Page.Resources> <local:CountToColumnDefinitionsConverter x:Key="CountToColumnsConverter"/> <local:IndexToGridColumnConverter x:Key="IndexToColumnConverter"/> </Page.Resources> <ItemsControl x:Name="Self" ItemsSource="{x:Bind ViewModel.SmtState.LoadTypes}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid ColumnDefinitions="{Binding Path=Items.Count, Converter={StaticResource CountToColumnsConverter}, ElementName=Self}"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter"> <Setter Property="Grid.Column" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource IndexToColumnConverter}}"/> <Setter Property="HorizontalAlignment" Value="Stretch"/> <Setter Property="VerticalAlignment" Value="Stretch"/> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemTemplate> <DataTemplate> <!-- 你的子项模板内容 --> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
方案二:自定义EqualWidthHorizontalLayout
直接自定义布局类,强制所有子项在一行平分可用宽度,不换行,适配任意数量子项,适合配合ItemsRepeater使用。
自定义布局代码
public class EqualWidthHorizontalLayout : Layout { protected override Size MeasureOverride(LayoutContext context, Size availableSize) { int itemCount = context.ItemCount; if (itemCount == 0) return new Size(0, 0); double itemWidth = availableSize.Width / itemCount; double maxItemHeight = 0; foreach (var child in context.Children) { child.Measure(new Size(itemWidth, availableSize.Height)); maxItemHeight = Math.Max(maxItemHeight, child.DesiredSize.Height); } return new Size(availableSize.Width, maxItemHeight); } protected override Size ArrangeOverride(LayoutContext context, Size finalSize) { int itemCount = context.ItemCount; if (itemCount == 0) return finalSize; double itemWidth = finalSize.Width / itemCount; double currentX = 0; foreach (var child in context.Children) { child.Arrange(new Rect(currentX, 0, itemWidth, finalSize.Height)); currentX += itemWidth; } return finalSize; } }
XAML中使用
<ItemsRepeater ItemsSource="{x:Bind ViewModel.SmtState.LoadTypes}"> <ItemsRepeater.Layout> <local:EqualWidthHorizontalLayout /> </ItemsRepeater.Layout> <DataTemplate> <!-- 你的子项模板内容 --> </DataTemplate> </ItemsRepeater>
方案对比
- 方案一:基于原生Grid控件,兼容性好,无需额外布局类,但需要编写转换器,当集合元素数量极大时,过多的Grid列可能带来性能开销。
- 方案二:自定义布局逻辑更简洁,性能更优(尤其配合
ItemsRepeater的虚拟化特性),无需绑定列数,完全适配动态集合,是更推荐的方案。
注:原生
UniformGridLayout无法满足需求的原因是,它会在空间不足时自动换行,而你需要的是强制子项平分空间、挤压适应而非换行。
内容的提问来源于stack exchange,提问作者Blindy
相关产品推荐
相关产品推荐

