C#/WPF - 等量拉伸嵌套 ItemsControl 中的按钮以填充整个 space

C#/WPF - Equally stretch buttons in nested ItemsControl to fill in entire space

首先我有一个ItemsControl,目的是展示几组内容;在每个组内,还有另一个ItemsControl,目的是在里面显示几个Button

我的.xaml:

<Window x:Class="CX11TestSolution.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="768"
    Width="1024">
<ItemsControl ItemsSource="{Binding HahaList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Label Content="{Binding Name}" />
                <ItemsControl Grid.Row="1"
                              ItemsSource="{Binding ItemList}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <UniformGrid Columns="5" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Button Content="{Binding Name}" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="1" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

我的.xaml.cs:

public partial class MainWindow : Window
{
    public IEnumerable<SomeClass> HahaList { get; }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        HahaList = new List<SomeClass>()
        {
            new SomeClass("Group 1", new List<SomeClass>() {
                new SomeClass("11"),
                new SomeClass("12"),
                new SomeClass("13"),
                new SomeClass("14"),
                new SomeClass("15"),
                new SomeClass("16"),
            }),
            new SomeClass("Group 2", new List<SomeClass>() {
                new SomeClass("21"),
                new SomeClass("22"),
                new SomeClass("23"),
                new SomeClass("24"),
            }),
            new SomeClass("Group 3", new List<SomeClass>() {
                new SomeClass("31"),
                new SomeClass("32"),
                new SomeClass("33"),
            }),
            new SomeClass("Group 4", new List<SomeClass>() {
                new SomeClass("41"),
                new SomeClass("42"),
            }),
            new SomeClass("Group 5", new List<SomeClass>() {
                new SomeClass("52"),
            }),
        };
    }
}

public class SomeClass
{
    public string Name { get; }
    public IEnumerable<SomeClass> ItemList { get; }
    public SomeClass(string name, IEnumerable<SomeClass> list)
    {
        Name = name;
        ItemList = list;
    }
    public SomeClass(string name)
    {
        Name = name;
    }
}

结果:

所以我的问题是如何平均拉伸按钮的高度,以便它们填满整个区域?

我实际上已经尝试将父 ItemsControlItemsPanel 设置为 UniformGrid,其中的 Columns 为 1,当数字为每组按钮的个数不超过5个,当超过5个时,第6个按钮会跳到下一行,从而缩小第一行,使每组高度相等。 提前谢谢你。

我会使用网格手动执行此操作。您知道最多有 5 列,因此很容易准确计算出包含所有内容的网格所需的列数和行数。使用附加行为,以便您可以在 运行 时动态设置网格的行和列,您还需要一个标志来指定每一行是 "Auto"(headers)还是一个“1*”(等距)。最后,为网格列和行号向 SomeClass 添加字段,并绑定到 XAML.

中的字段

可能有一种在纯 XAML 或自定义布局 class 中执行此操作的方法,但我上面描述的方法应该只需要 10 分钟左右的时间来编写代码,而且很容易测试和调试,所以你可能会在漫长的 运行.

中省去很多麻烦

更新:

我刚刚实现了我的方法,它需要你将它添加到你的主视图模型中:

    public int RowCount { get; set; }
    public int ColumnCount {get; set; }     
    public string StarCount { get; set; }
    public string StarRows { get; set; }
    public string StarColumns { get; set; }
    public List<Tuple<SomeClass, bool>> AllItems { get; set; } // second parameter indicates whether it's a header


    private void UpdateGridLayout()
    {
        this.AllItems = new List<Tuple<SomeClass, bool>>();

        this.ColumnCount = 5;
        this.RowCount = HahaList.Sum(x => 1 + (x.ItemList.Count() + this.ColumnCount - 1) / this.ColumnCount);
        int row = 0;
        this.StarColumns = String.Join(",", Enumerable.Range(0, this.ColumnCount).Select(i => i.ToString())); // all columns
        this.StarRows = null;
        foreach (var section in this.HahaList)
        {
            this.AllItems.Add(new Tuple<SomeClass, bool>(section, true));
            section.Row = row;
            section.Column = 0;
            section.ColumnSpan = this.ColumnCount;

            row++;
            if (StarRows != null)
                StarRows += ",";
            StarRows += row.ToString();
            int column = 0;             
            foreach (var item in section.ItemList)
            {
                this.AllItems.Add(new Tuple<SomeClass, bool>(item, false));
                item.Row = row;
                item.Column = column++;
                item.ColumnSpan = 1;
                if (column >= this.ColumnCount)
                {                       
                    column = 0;
                    row++;
                    if (StarRows != null)
                        StarRows += ",";
                    StarRows += row.ToString();
                }

            }
            row++;
        }
        return;
    }

设置 HahaList 后需要调用 UpdateGridLayout,如果您希望它支持动态更改,那么显然您必须完成所有操作并添加 INPC。此代码中的一个显着差异是添加了 "AllItems" 属性,它同时具有 headers 和叶节点以及指示每个节点是哪种类型的标志(我会使用不同的 class我自己,这样你就不必这样做了)。

SomeClass 还需要一些额外的属性:

    public int Row { get; set; }
    public int Column { get; set; }
    public int ColumnSpan { get; set; }

这里是 XAML:

<ItemsControl ItemsSource="{Binding AllItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid local:GridHelpers.RowCount="{Binding RowCount}"
                  local:GridHelpers.ColumnCount="{Binding ColumnCount}"
                  local:GridHelpers.StarRows="{Binding StarRows}"
                  local:GridHelpers.StarColumns="{Binding StarColumns}"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style>
            <Setter Property="Grid.Row" Value="{Binding Item1.Row}" />
            <Setter Property="Grid.Column" Value="{Binding Item1.Column}" />
            <Setter Property="Grid.ColumnSpan" Value="{Binding Item1.ColumnSpan}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ContentControl>
                <ContentControl.Style>
                    <Style TargetType="ContentControl">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate>
                                    <Label Content="{Binding Item1.Name}" />
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Item2}" Value="false">
                                <Setter Property="Template">
                                    <Setter.Value>
                                        <ControlTemplate>
                                            <Button Content="{Binding Item1.Name}" />
                                        </ControlTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ContentControl.Style>
            </ContentControl>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

XAML 使用了一个名为 GridHelpers 的 class 来协助创建动态行和列,您可以从 Rachael Lim's Blog.

获取源代码

结果:

这是可以使用Viewbox的地方,但它有副作用。

更改按钮上的 MinWidth 以获得您喜欢的 Label 与 Button 比率,因为当 windows 缩小时设计会受到影响,但当尺寸增加时没问题。

代码

<Viewbox Stretch="Fill">
    <ItemsControl ItemsSource="{Binding HahaList}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition/>
                    </Grid.RowDefinitions>
                    <Label Content="{Binding Name}"/>
                    <ItemsControl Grid.Row="1"
                          ItemsSource="{Binding ItemList}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <UniformGrid Columns="5"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Button Content="{Binding Name}" MinWidth="75"/>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Viewbox>