WPF 的 WrapPanel 换行后控件的对齐方式

前景:

我需要一个WrapPanel,里面有很多的控件,但是,WrapPanel 换行后是左对齐的,问题是我可能就3个控件,下面一个控件又有点长,左对齐太丑了,于是找了一下解决方案。哎,您说巧不巧,没搜到......

内心痛骂,某某dn真不愧是业界毒瘤,搜了半天,一个成用的没有,全是ctrl c + ctrl v,cv也行啊,但是不解决问题。真乌鸡鲅鱼,于是就有了这篇文章。

秉承着能用就行,能跑就行,又不是不能用的原则和方针,代码直接放到最后,拿来就用。

思路:

通过自定义控件的方式实现这个WrapPanel,然后引用控件,用法还是WrapPanel的正常用法,无非就是多加了个依赖属性,重写了一下ArrangeOverride,下面是具体思路:

1. 定义对齐方式的枚举

首先,定义一个枚举 LineAlignment 来表示行内元素的对齐方式。这个枚举包含三种值:Left、Center 和 Right,用于表示左对齐、居中对齐和右对齐。

2. 创建自定义的 WrapPanel

继承 WrapPanel 类,创建一个自定义的 AlignedWrapPanel,并添加一个依赖属性LineAlignment,用于在 XAML 或代码中指定对齐方式。

3. 重写 ArrangeOverride 方法

ArrangeOverride 方法是 WPF 布局系统的一部分,负责排列子元素。在这个方法中,你需要遍历所有子元素,并按顺序计算它们的位置。

在计算子元素位置时,需要判断当前行剩余的空间。如果当前行已满,或者即将换行,则根据对齐方式调整这一行的元素位置。

4. 实现行内元素的对齐逻辑

通过一个辅助方法 AdjustLine 来调整当前行中元素的水平位置:

Left 对齐:子元素从左到右排列,无需额外调整。

Center 对齐:计算行内元素的总宽度,并将剩余空间平均分配到行内每个元素的前面,以实现居中效果。

Right 对齐:计算行内元素的总宽度,并将剩余空间全部分配到行内第一个元素的前面,使得整行右对齐。

5. 处理换行

在 ArrangeOverride 中,遍历每个子元素时,如果当前行的宽度超出了 WrapPanel 的可用宽度,则需要换行,并在换行后调用 AdjustLine 方法来调整上一行的子元素对齐。

代码:

 public enum LineAlignment
 {
     Left,
     Center,
     Right
 }

 public class AlignedWrapPanel : WrapPanel
 {
     public static readonly DependencyProperty LineAlignmentProperty =
         DependencyProperty.Register(
             nameof(LineAlignment),
             typeof(LineAlignment),
             typeof(AlignedWrapPanel),
             new FrameworkPropertyMetadata(LineAlignment.Left, FrameworkPropertyMetadataOptions.AffectsArrange));

     public LineAlignment LineAlignment
     {
         get => (LineAlignment)GetValue(LineAlignmentProperty);
         set => SetValue(LineAlignmentProperty, value);
     }

     protected override Size ArrangeOverride(Size finalSize)
     {
         double currentLineHeight = 0;
         double currentX = 0;
         double currentY = 0;

         foreach (UIElement child in InternalChildren)
         {
             Size childDesiredSize = child.DesiredSize;

             if (currentX + childDesiredSize.Width > finalSize.Width)
             {
                 // 根据对齐方式调整行内子元素的起始位置
                 AdjustLine(finalSize.Width, ref currentX, ref currentY, currentLineHeight);

                 currentX = 0;
                 currentY += currentLineHeight;
                 currentLineHeight = 0;
             }

             child.Arrange(new Rect(new Point(currentX, currentY), childDesiredSize));
             currentX += childDesiredSize.Width;
             currentLineHeight = Math.Max(currentLineHeight, childDesiredSize.Height);
         }

         // 调整最后一行的元素对齐
         AdjustLine(finalSize.Width, ref currentX, ref currentY, currentLineHeight);

         return finalSize;
     }

     private void AdjustLine(double panelWidth, ref double currentX, ref double currentY, double currentLineHeight)
     {
         double spaceLeft = panelWidth - currentX;
         double offset = 0;

         switch (LineAlignment)
         {
             case LineAlignment.Center:
                 offset = spaceLeft / 2;
                 break;
             case LineAlignment.Right:
                 offset = spaceLeft;
                 break;
             case LineAlignment.Left:
             default:
                 offset = 0;
                 break;
         }

         if (offset > 0)
         {
             foreach (UIElement lineChild in InternalChildren)
             {
                 Rect arrangeRect = lineChild.TransformToAncestor(this).TransformBounds(new Rect(lineChild.RenderSize));
                 if (arrangeRect.Top >= currentY && arrangeRect.Top < currentY + currentLineHeight)
                 {
                     lineChild.Arrange(new Rect(new Point(arrangeRect.X + offset, arrangeRect.Y), lineChild.DesiredSize));
                 }
             }
         }
     }
 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值