当还有额外的 space 剩余时,如何填充 ItemsControl 的一行?

How to fill a row of an ItemsControl when there is still an extra space remaining?

我的目标是在 ItemsControl 中显示我的数据时有 3 列 。我发现关于这个问题的大部分答案都是使用 UniformGrid。像这样:

<ItemsControl x:Name="Tasks"
                ItemsSource="{Binding Tasks}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="3"
                            Background="Green" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button  Margin="0,10,0,16"
                        Padding="18,5,18,11"
                        Height="153"
                        Width="192"
                        Background="{StaticResource Card}"
                        Style="{StaticResource PlainButtonTheme}">
                        
                <!--Button Content-->
                
            </Button>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我的 UI 现在看起来像这样(我只是添加了绿色背景,所以我可以看到整个 UniformGrid):


我在这里想要的是我的中间列来占据整个 space(有点像网格的 * 行为)。我怎样才能做到这一点?

I tried using Grid before I used UniformGrid but my child controls just get stacked on top on each other, so after rendering I can only see one control.

需要明确设置单元格的地址(行和列)。

示例:

namespace GridItems
{
    public class TileData
    {
        public object Content { get; set; }
        public int Row { get; set; }
        public int Column { get; set; }

        public TileData() { }

        public TileData(object content, int row, int column)
        {
            Content = content;
            Row = row;
            Column = column;
        }
    }
}
namespace GridItems
{
    public class TilesViewModel
    {
        public TileData[] Tiles { get; } =
        {
            new TileData("First", 0, 0),
            new TileData("Second", 0, 1),
            new TileData("Third", 0, 2),
            new TileData("Fourth", 1, 0),
            new TileData("Fifth", 1, 1),
            new TileData("Sixth", 1, 2)
        };
    }
}
<Window x:Class="GridItems.GridItemsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GridItems"
        mc:Ignorable="d"
        Title="GridItemsWindow" Height="450" Width="800">
    <FrameworkElement.DataContext>
        <local:TilesViewModel/>
    </FrameworkElement.DataContext>
    <FrameworkElement.Resources>
        <DataTemplate x:Key="TileTemplate" DataType="local:TileData">
            <Viewbox Margin="20">
                <TextBlock Text="{Binding Content}"
                           FontSize="20"
                           Padding="20"
                           Background="Aqua"/>
            </Viewbox>
        </DataTemplate>
        <Style x:Key="ItemStyle" TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding Row}"/>
            <Setter Property="Grid.Column" Value="{Binding Column}"/>
        </Style>
        <ItemsPanelTemplate x:Key="GridTemplate">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"/>
                    <ColumnDefinition />
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </FrameworkElement.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding Tiles}"
                      ItemTemplate="{DynamicResource TileTemplate}"
                      ItemContainerStyle="{DynamicResource ItemStyle}"
                      ItemsPanel="{DynamicResource GridTemplate}"/>
    </Grid>
</Window>

补充:回答关于动态改变collection中元素数量的问题,相应地,Grid行。

使用附加的实施选项 属性。

using System;
using System.Windows;
using System.Windows.Controls;

namespace GridItems
{
    public static class AutoRowsGrid
    {
        /// <summary> Returns the value of the ChildrenCount attached property for <paramref name = "grid" />. </summary>
        /// <param name = "grid"> <see cref = "Grid" /> whose property value will be returned. </param>
        /// <returns> <see cref = "int" /> property value. </returns>
        public static int? GetChildrenCount(Grid grid)
        {
            return (int?)grid.GetValue(ChildrenCountProperty);
        }

        /// <summary> Sets the ChildrenCount attached property for <paramref name = "grid" />. </summary>
        /// <param name = "grid"> <see cref = "Grid" /> whose property value will be returned. </param>
        /// <param name = "value"> <see cref = "int" /> value for the property. </param>
        public static void SetChildrenCount(Grid grid, int value)
        {
            grid.SetValue(ChildrenCountProperty, value);
        }

        /// <summary><see cref="DependencyProperty"/> for methods <see cref="GetChildrenCount(Grid)"/> и <see cref="SetChildrenCount(Grid, int)"/>.</summary>
        public static readonly DependencyProperty ChildrenCountProperty =
            DependencyProperty.RegisterAttached(nameof(GetChildrenCount).Substring(3), typeof(int?), typeof(AutoRowsGrid), new PropertyMetadata(null, CountChanged));

        private static void CountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Grid grid = (Grid)d;
            double count = (int)e.NewValue;

            int columns = grid.ColumnDefinitions.Count;
            if (columns < 1)
                columns = 1;

            int newRows = (int)Math.Ceiling(count / columns);

            int rows = grid.RowDefinitions.Count;

            if (newRows != rows)
            {
                if (newRows > rows)
                {
                    for (; newRows > rows; rows++)
                    {
                        grid.RowDefinitions.Add(new RowDefinition());
                    }
                }
                else
                {
                    for (rows--; newRows <= rows; rows--)
                    {
                        grid.RowDefinitions.RemoveAt(rows);
                    }
                }
            }
        }
    }
}
<Window x:Class="GridItems.GridItemsWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:GridItems"
        mc:Ignorable="d"
        Title="GridItemsWindow" Height="450" Width="800">
    <FrameworkElement.DataContext>
        <local:TilesViewModel/>
    </FrameworkElement.DataContext>
    <FrameworkElement.Resources>
        <DataTemplate x:Key="TileTemplate" DataType="local:TileData">
            <Viewbox Margin="20">
                <TextBlock Text="{Binding Content}"
                           FontSize="20"
                           Padding="20"
                           Background="Aqua"/>
            </Viewbox>
        </DataTemplate>
        <Style x:Key="ItemStyle" TargetType="ContentPresenter">
            <Setter Property="Grid.Row" Value="{Binding Row}"/>
            <Setter Property="Grid.Column" Value="{Binding Column}"/>
        </Style>
        <ItemsPanelTemplate x:Key="GridTemplate">
            <Grid local:AutoRowsGrid.ChildrenCount="{Binding Items.Count, ElementName=listBox}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"/>
                    <ColumnDefinition />
                    <ColumnDefinition Width="100"/>
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </FrameworkElement.Resources>
    <Grid>
        <ItemsControl x:Name="listBox"
                      ItemsSource="{Binding Tiles}"
                      ItemTemplate="{DynamicResource TileTemplate}"
                      ItemContainerStyle="{DynamicResource ItemStyle}"
                      ItemsPanel="{DynamicResource GridTemplate}"/>
    </Grid>
</Window>

但如果你做得“相当正确”,那么在你的collection中,它的元素就会有明显的异质性。
左列、中列和右列的项目需要不同的呈现方式。
因此,最正确的,基于OOP的原则,是创建一个包含一行所有元素的类型,然后用行填充collection,而不是单独的单元格。