You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改ItemsControl、ListBox的填充方向为左下到右上?

实现WPF ItemsControl从左下到右上排列项

我明白你想要实现的是让ItemsControl里的项从左下到右上填充,而默认的UniformGrid做不到这个逻辑,不过我们可以通过自定义UniformGrid控件来搞定这个需求。

你当前的XAML代码

先贴出你现有的布局代码,方便对照参考:

<Grid> 
    <ItemsControl ItemsSource="{Binding TheList}"> 
        <ItemsControl.ItemsPanel> 
            <ItemsPanelTemplate> 
                <local:UniformGridWithOrientation Columns="2" /> 
            </ItemsPanelTemplate> 
        </ItemsControl.ItemsPanel> 
        <ItemsControl.ItemTemplate> 
            <DataTemplate> 
                <Border BorderBrush="Beige" BorderThickness="2" Background="AliceBlue" Margin="5"> 
                    <Grid Margin="5"> 
                        <TextBlock Text="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center" /> 
                    </Grid> 
                </Border> 
            </DataTemplate> 
        </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</Grid>

效果说明

当前效果(图A)是项从左上开始向右下依次排列,而你想要的目标效果(图B)是从左下角开始,向右排列填满一行后,向上换行继续排列。

解决方案:自定义UniformGridWithOrientation控件

下面是实现这个特殊排列逻辑的自定义控件代码,核心是重写ArrangeOverride方法来调整项的排列位置:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls.Primitives;

namespace ListOrientation
{
    public class UniformGridWithOrientation : UniformGrid
    {
        #region Orientation (Dependency Property)
        public static readonly DependencyProperty OrientationProperty =
            DependencyProperty.Register("Orientation", typeof(System.Windows.Controls.Orientation), 
                typeof(UniformGridWithOrientation), new FrameworkPropertyMetadata(
                    System.Windows.Controls.Orientation.Vertical, 
                    FrameworkPropertyMetadataOptions.AffectsMeasure), 
                new ValidateValueCallback(UniformGridWithOrientation.IsValidOrientation));

        internal static bool IsValidOrientation(object o)
        {
            System.Windows.Controls.Orientation orientation = (System.Windows.Controls.Orientation)o;
            return orientation == System.Windows.Controls.Orientation.Vertical || 
                   orientation == System.Windows.Controls.Orientation.Horizontal;
        }

        public System.Windows.Controls.Orientation Orientation
        {
            get { return (System.Windows.Controls.Orientation)GetValue(OrientationProperty); }
            set { SetValue(OrientationProperty, value); }
        }
        #endregion

        protected override Size MeasureOverride(Size constraint)
        {
            this.UpdateComputedValues();
            Size availableSize = new Size(constraint.Width / ((double)this._columns), constraint.Height / ((double)this._rows));
            double width = 0.0;
            double height = 0.0;
            int num3 = 0;
            int count = base.InternalChildren.Count;
            while (num3 < count)
            {
                UIElement element = base.InternalChildren[num3];
                element.Measure(availableSize);
                Size desiredSize = element.DesiredSize;
                if (width < desiredSize.Width)
                {
                    width = desiredSize.Width;
                }
                if (height < desiredSize.Height)
                {
                    height = desiredSize.Height;
                }
                num3++;
            }
            return new Size(width * this._columns, height * this._rows);
        }

        private void UpdateComputedValues()
        {
            this._columns = this.Columns;
            this._rows = this.Rows;
            if (this.FirstColumn >= this._columns)
            {
                this.FirstColumn = 0;
            }
            if (FirstColumn > 0) 
                throw new NotImplementedException("There is no support for setting the FirstColumn (nor the FirstRow).");

            if ((this._rows == 0) || (this._columns == 0))
            {
                int num = 0; // 统计可见子项数量
                int num2 = 0;
                int count = base.InternalChildren.Count;
                while (num2 < count)
                {
                    UIElement element = base.InternalChildren[num2];
                    if (element.Visibility != Visibility.Collapsed)
                    {
                        num++;
                    }
                    num2++;
                }
                if (num == 0)
                {
                    num = 1;
                }
                if (this._rows == 0)
                {
                    if (this._columns > 0)
                    {
                        this._rows = ((num + this.FirstColumn) + (this._columns - 1)) / this._columns;
                    }
                    else
                    {
                        this._rows = (int)Math.Sqrt((double)num);
                        if ((this._rows * this._rows) < num)
                        {
                            this._rows++;
                        }
                        this._columns = this._rows;
                    }
                }
                else if (this._columns == 0)
                {
                    this._columns = (num + (this._rows - 1)) / this._rows;
                }
            }
        }

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            Rect finalRect = new Rect(0.0, 0.0, arrangeSize.Width / ((double)this._columns), arrangeSize.Height / ((double)this._rows));
            double height = finalRect.Height;
            // 初始位置设为控件底部减去项的高度,也就是左下角起点
            double yPos = arrangeSize.Height - height;
            finalRect.X += finalRect.Width * this.FirstColumn;
            finalRect.Y += yPos;

            foreach (UIElement element in base.InternalChildren)
            {
                element.Arrange(finalRect);
                if (element.Visibility != Visibility.Collapsed)
                {
                    // 向右排列,一行排满后向上换行
                    finalRect.X += finalRect.Width;
                    if (finalRect.X >= arrangeSize.Width)
                    {
                        finalRect.Y -= height;
                        finalRect.X = 0;
                    }
                }
            }
            return arrangeSize;
        }

        private int _columns;
        private int _rows;
    }
}

核心逻辑说明

  • 我们继承了原生UniformGrid,保留了它的测量逻辑(MeasureOverride和UpdateComputedValues),确保项的尺寸计算和行列数量计算正确。
  • 关键修改在ArrangeOverride方法:把项的初始Y坐标设置为控件底部减去单个项的高度,这样第一个项会从左下角开始排列;每排完一个项就向右移动X坐标,当一行排满时,向上移动Y坐标(减去项的高度),并重置X坐标为0,实现从左下到右上的填充效果。

内容的提问来源于stack exchange,提问作者smanookian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:01:27