如何修改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
相关产品推荐
相关产品推荐

