简体   繁体   English

C#/ WPF-均匀拉伸嵌套ItemsControl中的按钮以填充整个空间

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

First of all, I have a ItemsControl , which aims to display several groups of contents; 首先,我有一个ItemsControl ,旨在显示几组内容; inside each group, there is another ItemsControl , which aims to display serveral Button inside. 每个组中都有另一个ItemsControl ,旨在在其中显示服务器Button

My .xaml: 我的.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>

My .xaml.cs: 我的.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;
    }
}

Result: 结果: 结果

So my question is that how do I equally stretch the height of the buttons so that they will fill in the entire area? 所以我的问题是,我如何平等地拉伸按钮的高度,以便它们可以填充整个区域?

I have actually tried to set the ItemsPanel of the parent ItemsControl to a UniformGrid which has a Columns of 1, and it does work when the number of buttons in each group is no more than 5. When it exceeds 5, the sixth button will jump to the next row, thus shrinking the first row to make each group have equal height. 事实上,我试图设置ItemsPanelItemsControlUniformGrid具有Columns的1,和它的工作时按钮的每个组中的数量不超过5,当其超过5,第六按钮将跳到下一行,因此缩小第一行以使每个组具有相同的高度。 在此处输入图片说明 Thank you in advance. 先感谢您。

I would do this manually with a grid. 我将使用网格手动执行此操作。 You know there are a max of 5 columns so it's easy to work out exactly how many columns and rows you'll need for a grid that encompasses everything. 您知道最多有5列,因此可以很容易地精确计算出包含所有内容的网格所需的列数和行数。 Use an attached behaviour so that you can set the grid's rows and columns dynamically at runtime, you'll also want a flag to specify whether each row is an "Auto" (headers) or a "1*" (equally spaced). 使用附加行为,以便您可以在运行时动态设置网格的行和列,还需要一个标志来指定每行是“自动”(标题)还是“ 1 *”(等距)。 Last, add fields to SomeClass for the Grid column and row number and bind to that in your XAML. 最后,将字段添加到SomeClass中以获取Grid列和行号,并将其绑定到XAML中。

There's probably a way of doing this in pure XAML or with a custom layout class but the method I describe above should only take 10 mins or so to code up and it's easy to test and debug, so you'll probably save yourself a lot of headache in the long run. 可能有一种在纯XAML或自定义布局类中执行此操作的方法,但是我上面描述的方法只需要10分钟左右的时间即可编写代码,并且易于测试和调试,因此您可能会节省很多时间从长远来看头痛。

UPDATE: 更新:

I've just implemented my method of doing this, it requires you to add this to your main view model: 我刚刚实现了执行此操作的方法,它要求您将其添加到主视图模型中:

    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;
    }

UpdateGridLayout needs to be called after you've set your HahaList, if you want this to support dynamic changes then obviously you'll have to go through it all and add INPC. 设置HahaList之后,需要调用UpdateGridLayout,如果您希望它支持动态更改,那么显然您必须全部经过并添加INPC。 One notable difference in this code is the addition of the "AllItems" property which has both headers and leaf nodes along with a flag indicating which type each one is (I would have used different classes myself so that you don't have to do this). 此代码中的一个显着区别是添加了“ AllItems”属性,该属性同时具有标头和叶节点,以及一个标志,指示每个类型是哪种类型(我自己会使用不同的类,因此您不必这样做)。

SomeClass also needs a few extra properties: SomeClass还需要一些额外的属性:

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

And here's the XAML: 这是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>

The XAML uses a class called GridHelpers which assists in creating dynamic rows and columns, you can get the source code from Rachael Lim's Blog . XAML使用一个名为GridHelpers的类,该类可帮助创建动态行和列,您可以从Rachael Lim的Blog中获取源代码。

Result: 结果:

在此处输入图片说明

This is where a Viewbox can be used, but it comes with side effects. 这是可以使用Viewbox地方,但有副作用。

Change MinWidth on the button to get the Label to Button ratio what you like because the design will suffer when the windows shrinks, but does okay when size increases. 更改按钮上的MinWidth即可获得所需的“标签与按钮”比率,因为当窗口缩小时设计会受到影响,但在尺寸增加时效果会很好。

使用者介面范例

Code

<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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM