C# 显示动态数量的控件

C# 显示动态数量的控件,c#,wpf,C#,Wpf,当显示动态数量的控件时,我在编写合理的逻辑时遇到问题,这些控件的数量可以是1到9之间的任意数字。因此,如果用户输入为1,则控件应尝试填充屏幕;如果用户输入为2,则两个控件应均匀分割屏幕;如果数字为3,则一个控件将显示在屏幕的顶部50%,而两个控件应分割屏幕的底部50%,以此类推 到目前为止,我提出的解决方案涉及在代码中生成大量网格行和列,根据用户输入,将控件分配给正确的行和列。然而,这种解决方案感觉像是一种黑客行为,会产生大量不必要的代码。如果我以后想扩展控件的数量,它也不是很灵活 我觉得必须有

当显示动态数量的控件时,我在编写合理的逻辑时遇到问题,这些控件的数量可以是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>