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

时间:2016-01-19 00:15:24

标签: c# wpf xaml itemscontrol

首先,我有一个ItemsControl,旨在显示几组内容;在每个组中,还有另一个ItemsControl,其目的是在内部显示Button个。{/ p>

我的.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;
    }
}

结果: Result

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

我实际上已经尝试将父ItemsPanel的{​​{1}}设置为ItemsControlUniformGrid的{​​{1}},当按钮数量确实有效时在每组中不超过5.当它超过5时,第六个按钮将跳到下一行,从而缩小第一行以使每组具有相等的高度。 enter image description here 提前谢谢。

2 个答案:

答案 0 :(得分:1)

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

在纯XAML中或者使用自定义布局类可能有一种方法可以做到这一点,但我上面描述的方法只需要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”属性,该属性包含头部和叶子节点以及一个标志,指示每个类型的类型(我自己会使用不同的类,因此您不必这样做)。

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的类来帮助创建动态行和列,您可以从Rachael Lim's Blog获取源代码。

结果:

enter image description here

答案 1 :(得分:0)

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

更改按钮上的MinWidth以获得你喜欢的Label to Button比率,因为当窗口缩小时设计会受到影响,但是当尺寸增加时,设计会正常。

Sample UI

<强>代码

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