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;
}
}
结果:
所以我的问题是如何平均拉伸按钮的高度,以便它们填满整个区域?
我实际上已经尝试将父 ItemsControl
的 ItemsPanel
设置为 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>
首先我有一个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;
}
}
结果:
所以我的问题是如何平均拉伸按钮的高度,以便它们填满整个区域?
我实际上已经尝试将父 ItemsControl
的 ItemsPanel
设置为 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>