自定义代有换行功能的Panel
WrapBreakPanel.cs
using System; using System.Collections.Generic; using System.Text; using System.Windows.Controls; using System.Windows; namespace MyPanels { public class WrapBreakPanel:Panel { public static readonly DependencyProperty LineBreakBeforeProperty; static WrapBreakPanel() { FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata(); metadata.AffectsArrange = true; metadata.AffectsMeasure = true; LineBreakBeforeProperty = DependencyProperty.RegisterAttached("LineBreakBefore", typeof(bool), typeof(WrapBreakPanel), metadata); } public static Boolean GetLineBreakBefore(UIElement element) { return (bool)element.GetValue(LineBreakBeforeProperty); } public static void SetLineBreakBefore(UIElement element, Boolean value) { element.SetValue(LineBreakBeforeProperty, value); } /// <summary> /// 测量阶段 /// 测量WrapBreakPanel 所要的期望大小(height,width) /// Desired Size ≤ Actual Size ≤ Available Size /// Desired Size:期望尺寸是子元素想要的尺寸; 对整个UI页面的检测,并询问每个元素的期望尺寸 /// Actual Size:实际尺寸是父元元素分配给子元素的最终尺寸。 /// Avilable Size:可用尺寸是测量阶段的初始约束值,即父元素愿意给子元素的最大空间值; /// /// </summary> /// <param name="availableSize">可用大小(height,width)</param> /// <returns>所要的期望大小(height,width)</returns> protected override Size MeasureOverride(Size availableSize) { Size currentLineSize = new Size(); Size panelSize = new Size(); foreach (UIElement element in base.InternalChildren) { //1、传入可以用大小,计算完后得到元素的期望值 询问孩子:你想要多大的空间显示自己? element.Measure(availableSize); ///2、获取元素的期望大小 孩子接到询问后,根据父给的availableSize以及自己的一些限制,比如Margin,Width,等等, ///孩子回答:我想要XXX大小的空间。 Size desiredSize = element.DesiredSize; //3、父拿到孩子给的期望的空间大小后,根据自己的策略开始真正给孩子分配空间,就进入第二个子过程。 //判断此元素是否是 断点元素 if (GetLineBreakBefore(element) || currentLineSize.Width + desiredSize.Width > availableSize.Width) { //切换到新行(要么是因为元素请求了它,要么是因为空间用完了)。 panelSize.Width = Math.Max(currentLineSize.Width, panelSize.Width); panelSize.Height += currentLineSize.Height; currentLineSize = desiredSize; //如果元素太宽,不能使用最大宽度的行,只需给它一个单独的行。 if (desiredSize.Width > availableSize.Width) { panelSize.Width = Math.Max(desiredSize.Width, panelSize.Width); panelSize.Height += desiredSize.Height; currentLineSize = new Size(); } } else { //继续添加到当前行。 currentLineSize.Width += desiredSize.Width; //确保该行与它的最高元素一样高。 currentLineSize.Height = Math.Max(desiredSize.Height, currentLineSize.Height); } } panelSize.Width = Math.Max(currentLineSize.Width, panelSize.Width); panelSize.Height += currentLineSize.Height; return panelSize; } /// <summary> /// /// </summary> /// <param name="arrangeBounds"></param> /// <returns>实即大小</returns> protected override Size ArrangeOverride(Size arrangeBounds) { int firstInLine = 0; Size currentLineSize = new Size(); double accumulatedHeight = 0; UIElementCollection elements = base.InternalChildren; for (int i = 0; i < elements.Count; i++) { Size desiredSize = elements[i].DesiredSize; if (GetLineBreakBefore(elements[i]) || currentLineSize.Width + desiredSize.Width > arrangeBounds.Width) //need to switch to another line { arrangeLine(accumulatedHeight, currentLineSize.Height, firstInLine, i); accumulatedHeight += currentLineSize.Height; currentLineSize = desiredSize; if (desiredSize.Width > arrangeBounds.Width) //the element is wider then the constraint - give it a separate line { arrangeLine(accumulatedHeight, desiredSize.Height, i, ++i); accumulatedHeight += desiredSize.Height; currentLineSize = new Size(); } firstInLine = i; } else //continue to accumulate a line { currentLineSize.Width += desiredSize.Width; currentLineSize.Height = Math.Max(desiredSize.Height, currentLineSize.Height); } } if (firstInLine < elements.Count) arrangeLine(accumulatedHeight, currentLineSize.Height, firstInLine, elements.Count); return arrangeBounds; } private void arrangeLine(double y, double lineHeight, int start, int end) { double x = 0; UIElementCollection children = InternalChildren; for (int i = start; i < end; i++) { UIElement child = children[i]; child.Arrange(new Rect(x, y, child.DesiredSize.Width, lineHeight)); x += child.DesiredSize.Width; } } } }
xaml.cs
<Window x:Class="WrapBreakPanel.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 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" xmlns:local="clr-namespace:WrapBreakPanel" xmlns:my="clr-namespace:MyPanels" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <StackPanel> <StackPanel.Resources> <Style TargetType="{x:Type Button}"> <Setter Property="Margin" Value="3"></Setter> <Setter Property="Padding" Value="5"/> </Style> </StackPanel.Resources> <TextBlock Padding="5" Background="LightGray">Content above the WrapBreakPanel.</TextBlock> <my:WrapBreakPanel> <Button >No1 Break Here</Button> <Button my:WrapBreakPanel.LineBreakBefore="True" FontWeight="Bold">No2 Break Here</Button> <Button>No3 Break Here</Button> <Button>No4 Break Here</Button> <Button my:WrapBreakPanel.LineBreakBefore="True" FontWeight="Bold">Button with Break</Button> <Button>No4 Break Here</Button> <Button>No5 Break Here</Button> <Button>No6 Break Here</Button> <Button>No7 Break Here</Button> </my:WrapBreakPanel> <TextBlock Padding="5" Background="LightGray">Content below the WrapBreakPanel.</TextBlock> </StackPanel> </Grid> </Window>
效果
标签:控件,currentLineSize,自定义,WrapBeakPanel,panelSize,Height,Width,desiredSize,Size From: https://www.cnblogs.com/cdaniu/p/16931239.html