C# 显示动态数量的控件
当显示动态数量的控件时,我在编写合理的逻辑时遇到问题,这些控件的数量可以是1到9之间的任意数字。因此,如果用户输入为1,则控件应尝试填充屏幕;如果用户输入为2,则两个控件应均匀分割屏幕;如果数字为3,则一个控件将显示在屏幕的顶部50%,而两个控件应分割屏幕的底部50%,以此类推 到目前为止,我提出的解决方案涉及在代码中生成大量网格行和列,根据用户输入,将控件分配给正确的行和列。然而,这种解决方案感觉像是一种黑客行为,会产生大量不必要的代码。如果我以后想扩展控件的数量,它也不是很灵活C# 显示动态数量的控件,c#,wpf,C#,Wpf,当显示动态数量的控件时,我在编写合理的逻辑时遇到问题,这些控件的数量可以是1到9之间的任意数字。因此,如果用户输入为1,则控件应尝试填充屏幕;如果用户输入为2,则两个控件应均匀分割屏幕;如果数字为3,则一个控件将显示在屏幕的顶部50%,而两个控件应分割屏幕的底部50%,以此类推 到目前为止,我提出的解决方案涉及在代码中生成大量网格行和列,根据用户输入,将控件分配给正确的行和列。然而,这种解决方案感觉像是一种黑客行为,会产生大量不必要的代码。如果我以后想扩展控件的数量,它也不是很灵活 我觉得必须有
我觉得必须有一种更简单的方法来解决这个问题,有什么建议吗?你可以对每一行使用一个
堆栈面板,对你的“网格”使用一个,就像这样
<StackPanel>
<StackPanel/>
<StackPanel/>
</StackPanel>
然后,您可以根据需要添加和填充行,以匹配给定配置的布局-例如,对于3个控件-第1行中的2个和第2行中的1个,对于4个控件-第1行中的2个和第2行中的2个,等等
然后可以将每行中控件的宽度绑定到一个属性,该属性是该特定行中有多少控件的函数。例如,如果一行中有3个控件,则每个控件将是宽度的1/3。只要在修改行时更新此宽度属性,控件宽度就会更新以填充可用空间
要添加其他布局,您只需向布局配置中添加新规则。您可以扩展网格
或类似控件,并覆盖自定义逻辑的布局行为,而无需重新发明控制盘
例如,可以按以下方式创建动态栅格控件(该控件可用于任意数量的子控件,并自动调整行数和列数):
公共类DynamicGrid:Grid
{
公共静态只读DependencyProperty AdjustColumnWidthProperty=
DependencyProperty.RegisterAttached(“AdjustColumnWidth”,
类型(双),
类型(动态网格),
新的FrameworkPropertyMetadata(1.0,FrameworkPropertyMetadata选项.affectsRange));
公共静态双GetAdjustColumnWidth(DependencyObject d)
{
返回(双)d.GetValue(AdjustColumnWidthProperty);
}
公共静态void SetAdjustColumnWidth(DependencyObject d,双值)
{
d、 设置值(AdjustColumnWidthProperty,value);
}
私有整数getSquareLength(整数项)
{
双重结果=数学Sqrt(项目);
返回(整数)数学上限(结果);
}
私有整型getColumns(整型长度)
{
返回长度;
}
私有整型getRows(整型长度)
{
变量计数=_currentChildrenCount;
//假设我们可以有空行
变量行=长度-1;
//如果符合条件-太好了!
如果(行*长度>=计数)
返回行;
其他的
返回行+1;
}
私有int_currentChildrenCount;
私有无效OnNumberOfItemsChangedImpl()
{
var numfochildren=\u currentChildrenCount;
使用(var d=Dispatcher.DisableProcessing())
{
RowDefinitions.Clear();
ColumnDefinitions.Clear();
如果(numfochildren>0)
{
var squareLength=getSquareLength(numOfChildren);
var numocols=getColumns(squareLength);
var numorrows=getRows(squareLength);
对于(var i=0;i=numofols)
{
col=0;
行++;
}
}
}
}
}
受保护的替代尺寸ArrangeOverride(尺寸arrangeSize)
{
var toReturn=base.ArrangeOverride(arrangeSize);
foreach(子对象中的变量视图)
{
var cell=(FrameworkElement)视图;
var adjustWidthFactor=GetAdjustColumnWidth(单元格);
var bounds=LayoutInformation.GetLayoutSlot(单元格);
var newBounds=new Rect(
x:bounds.Width*adjustWidthFactor*GetColumn(单元格),
y:边界,顶部,
宽度:边界。宽度*调整宽度因子,
高度:界限。高度
);
单元排列(新边界);
}
回归回归;
}
公共动态网格()
{
_currentChildrenCount=0;
布局更新+=(s,e)=>{
如果(儿童?.Count!=\u当前儿童计数)
{
_currentChildrenCount=(Children!=null)?childrenCount。计数:0;
public class DynamicGrid : Grid
{
public static readonly DependencyProperty AdjustColumnWidthProperty =
DependencyProperty.RegisterAttached("AdjustColumnWidth",
typeof(double),
typeof(DynamicGrid),
new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsArrange));
public static double GetAdjustColumnWidth(DependencyObject d)
{
return (double)d.GetValue(AdjustColumnWidthProperty);
}
public static void SetAdjustColumnWidth(DependencyObject d, double value)
{
d.SetValue(AdjustColumnWidthProperty, value);
}
private int getSquareLength(int items)
{
double result = Math.Sqrt(items);
return (int)Math.Ceiling(result);
}
private int getColumns(int length)
{
return length;
}
private int getRows(int length)
{
var count = _currentChildrenCount;
//assume we can have empty row
var rows = length - 1;
//if fits the bill - great!
if (rows * length >= count)
return rows;
else
return rows + 1;
}
private int _currentChildrenCount;
private void OnNumberOfItemsChangedImpl()
{
var numOfChildren = _currentChildrenCount;
using (var d = Dispatcher.DisableProcessing())
{
RowDefinitions.Clear();
ColumnDefinitions.Clear();
if (numOfChildren > 0)
{
var squareLength = getSquareLength(numOfChildren);
var numOfCols = getColumns(squareLength);
var numOfRows = getRows(squareLength);
for (var i = 0; i < numOfRows; i++)
RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
for (var i = 0; i < numOfCols; i++)
ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
var adjustWidthFactor = 1.0;
var adjustWidthOnLastRow = numOfChildren < (numOfCols * numOfRows);
if (adjustWidthOnLastRow)
{
var notEmptySlots = (numOfChildren % numOfCols);
adjustWidthFactor = ((double)numOfCols / (double)notEmptySlots);
}
int row = 0, col = 0;
foreach (var view in Children)
{
var cell = (FrameworkElement)view;
SetRow(cell, row);
SetColumn(cell, col);
if (adjustWidthOnLastRow && row == (numOfRows - 1))
SetAdjustColumnWidth(cell, adjustWidthFactor);
else
SetAdjustColumnWidth(cell, 1.0);
if (++col >= numOfCols)
{
col = 0;
row++;
}
}
}
}
}
protected override Size ArrangeOverride(Size arrangeSize)
{
var toReturn = base.ArrangeOverride(arrangeSize);
foreach (var view in Children)
{
var cell = (FrameworkElement)view;
var adjustWidthFactor = GetAdjustColumnWidth(cell);
var bounds = LayoutInformation.GetLayoutSlot(cell);
var newBounds = new Rect(
x: bounds.Width * adjustWidthFactor * GetColumn(cell),
y: bounds.Top,
width: bounds.Width * adjustWidthFactor,
height: bounds.Height
);
cell.Arrange(newBounds);
}
return toReturn;
}
public DynamicGrid()
{
_currentChildrenCount = 0;
LayoutUpdated += (s, e) => {
if (Children?.Count != _currentChildrenCount)
{
_currentChildrenCount = (Children != null) ? Children.Count : 0;
OnNumberOfItemsChangedImpl();
}
};
}
}
<local:DynamicGrid Margin="20">
<Button>one</Button>
<Button>two</Button>
<Button>three</Button>
<Button>four</Button>
<Button>five</Button>
<Button>six</Button>
<Button>seven</Button>
<Button>eight</Button>
</local:DynamicGrid>
<ItemsControl Margin="20">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:DynamicGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Gray" Margin="5">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsSource>
<col:ArrayList>
<sys:String>one</sys:String>
<sys:String>two</sys:String>
<sys:String>three</sys:String>
<sys:String>four</sys:String>
<sys:String>five</sys:String>
</col:ArrayList>
</ItemsControl.ItemsSource>
</ItemsControl>
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ItemsControl>
<ItemsControl.ItemsSource>
<Binding Path="Value" ElementName="slider">
<Binding.Converter>
<local:CountToCollectionConverter />
</Binding.Converter>
</Binding>
</ItemsControl.ItemsSource>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="Gray" Margin="5">
<TextBlock Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:DynamicGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Slider x:Name="slider"
Grid.Row="1"
Minimum="1"
Maximum="12"
TickFrequency="1"
IsSnapToTickEnabled="True"
VerticalAlignment="Center" />
</Grid>