自己写一个WrapBreakPanel类就行了。

WrapBreakPanel.cs

using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using System;

namespace Shares.Avalonia.CustomControls
{
    public class WrapBreakPanel : Panel
    {
        // Orientation property
        public static readonly StyledProperty<Orientation> OrientationProperty =
            AvaloniaProperty.Register<WrapBreakPanel, Orientation>(nameof(Orientation), Orientation.Horizontal);

        public Orientation Orientation
        {
            get => GetValue(OrientationProperty);
            set => SetValue(OrientationProperty, value);
        }

        // Spacing property
        public static readonly StyledProperty<double> SpacingProperty =
            AvaloniaProperty.Register<WrapBreakPanel, double>(nameof(Spacing), 0);

        public double Spacing
        {
            get => GetValue(SpacingProperty);
            set => SetValue(SpacingProperty, value);
        }

        // LineBreakBefore
        public static readonly AttachedProperty<bool> LineBreakBeforeProperty =
            AvaloniaProperty.RegisterAttached<WrapBreakPanel, Control, bool>("LineBreakBefore");

        public static void SetLineBreakBefore(Control element, bool value)
            => element.SetValue(LineBreakBeforeProperty, value);

        public static bool GetLineBreakBefore(Control element)
            => element.GetValue(LineBreakBeforeProperty);

        // LineBreakAfter
        public static readonly AttachedProperty<bool> LineBreakAfterProperty =
            AvaloniaProperty.RegisterAttached<WrapBreakPanel, Control, bool>("LineBreakAfter");

        public static void SetLineBreakAfter(Control element, bool value)
            => element.SetValue(LineBreakAfterProperty, value);

        public static bool GetLineBreakAfter(Control element)
            => element.GetValue(LineBreakAfterProperty);

        protected override Size MeasureOverride(Size availableSize)
        {
            return Orientation == Orientation.Horizontal ?
                MeasureHorizontal(availableSize) :
                MeasureVertical(availableSize);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            return Orientation == Orientation.Horizontal ?
                ArrangeHorizontal(finalSize) :
                ArrangeVertical(finalSize);
        }

        // Horizontal measure
        private Size MeasureHorizontal(Size available)
        {
            Size currentLine = new();
            Size panelSize = new();

            foreach (var child in Children)
            {
                child.Measure(available);
                var desired = child.DesiredSize;

                bool breakBefore = GetLineBreakBefore(child);
                bool breakAfter = GetLineBreakAfter(child);

                double widthWithSpacing = desired.Width + (currentLine.Width > 0 ? Spacing : 0);

                if (breakBefore || currentLine.Width + widthWithSpacing > available.Width)
                {
                    // Add line spacing
                    if (panelSize.Height > 0)
                        panelSize = new Size(panelSize.Width, panelSize.Height + Spacing);

                    panelSize = new Size(
                        Math.Max(panelSize.Width, currentLine.Width),
                        panelSize.Height + currentLine.Height
                    );

                    currentLine = desired;
                }
                else
                {
                    currentLine = new Size(
                        currentLine.Width + widthWithSpacing,
                        Math.Max(currentLine.Height, desired.Height)
                    );
                }

                if (breakAfter)
                {
                    if (panelSize.Height > 0)
                        panelSize = new Size(panelSize.Width, panelSize.Height + Spacing);

                    panelSize = new Size(
                        Math.Max(panelSize.Width, currentLine.Width),
                        panelSize.Height + currentLine.Height
                    );

                    currentLine = new();
                }
            }

            return new Size(
                Math.Max(panelSize.Width, currentLine.Width),
                panelSize.Height + currentLine.Height
            );
        }

        // Horizontal arrange
        private Size ArrangeHorizontal(Size final)
        {
            double x = 0;
            double y = 0;
            double lineHeight = 0;

            foreach (var child in Children)
            {
                var sz = child.DesiredSize;

                bool breakBefore = GetLineBreakBefore(child);
                bool breakAfter = GetLineBreakAfter(child);

                double widthWithSpacing = sz.Width + (x > 0 ? Spacing : 0);

                if (breakBefore || (x + widthWithSpacing > final.Width))
                {
                    y += lineHeight;

                    if (y > 0)
                        y += Spacing;

                    x = 0;
                    lineHeight = 0;
                }

                child.Arrange(new Rect(x, y, sz.Width, sz.Height));

                x += sz.Width + Spacing;
                lineHeight = Math.Max(lineHeight, sz.Height);

                if (breakAfter)
                {
                    y += lineHeight + Spacing;
                    x = 0;
                    lineHeight = 0;
                }
            }

            return final;
        }

        // Vertical measure
        private Size MeasureVertical(Size available)
        {
            Size currentColumn = new();
            Size panelSize = new();

            foreach (var child in Children)
            {
                child.Measure(available);
                var desired = child.DesiredSize;

                bool breakBefore = GetLineBreakBefore(child);
                bool breakAfter = GetLineBreakAfter(child);

                double heightWithSpacing = desired.Height + (currentColumn.Height > 0 ? Spacing : 0);

                if (breakBefore || currentColumn.Height + heightWithSpacing > available.Height)
                {
                    if (panelSize.Width > 0)
                        panelSize = new Size(panelSize.Width + Spacing, panelSize.Height);

                    panelSize = new Size(
                        panelSize.Width + currentColumn.Width,
                        Math.Max(panelSize.Height, currentColumn.Height)
                    );

                    currentColumn = desired;
                }
                else
                {
                    currentColumn = new Size(
                        Math.Max(currentColumn.Width, desired.Width),
                        currentColumn.Height + heightWithSpacing
                    );
                }

                if (breakAfter)
                {
                    if (panelSize.Width > 0)
                        panelSize = new Size(panelSize.Width + Spacing, panelSize.Height);

                    panelSize = new Size(
                        panelSize.Width + currentColumn.Width,
                        Math.Max(panelSize.Height, currentColumn.Height)
                    );

                    currentColumn = new();
                }
            }

            return new Size(
                panelSize.Width + currentColumn.Width,
                Math.Max(panelSize.Height, currentColumn.Height)
            );
        }

        // Vertical arrange
        private Size ArrangeVertical(Size final)
        {
            double x = 0;
            double y = 0;
            double colWidth = 0;

            foreach (var child in Children)
            {
                var sz = child.DesiredSize;

                bool breakBefore = GetLineBreakBefore(child);
                bool breakAfter = GetLineBreakAfter(child);

                double heightWithSpacing = sz.Height + (y > 0 ? Spacing : 0);

                if (breakBefore || y + heightWithSpacing > final.Height)
                {
                    x += colWidth;

                    if (x > 0)
                        x += Spacing;

                    y = 0;
                    colWidth = 0;
                }

                child.Arrange(new Rect(x, y, sz.Width, sz.Height));

                y += sz.Height + Spacing;
                colWidth = Math.Max(colWidth, sz.Width);

                if (breakAfter)
                {
                    x += colWidth + Spacing;
                    y = 0;
                    colWidth = 0;
                }
            }

            return final;
        }
    }
}

WrapBreakPanelTest.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Height="300" Width="500" 
        x:Class="AvaloniaUI.WrapBreakPanelTest"
        Title="WrapBreakPanelTest">
    <WrapBreakPanel Spacing="5">
        <Button Content="A"/>
        <Button Content="B" WrapBreakPanel.LineBreakBefore="True"/>
        <Button Content="C"/>
    </WrapBreakPanel>
</Window>

WrapBreakPanelTest.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Shares;
using System;

namespace AvaloniaUI;

public partial class WrapBreakPanelTest : Window
{
    public WrapBreakPanelTest()
    {
        InitializeComponent();
       
    }
}

运行效果

image

 

posted on 2025-11-29 14:08  dalgleish  阅读(0)  评论(0)    收藏  举报