自己写一个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();
}
}
运行效果

浙公网安备 33010602011771号