我正在尝试使我认为是WPF中的一个简单面板,它具有以下属性:
我的面板如下所示:
public class MyStackPanel : Panel
{
protected override Size MeasureOverride(Size availableSize)
{
Size requiredSize = new Size();
foreach (UIElement e in InternalChildren)
{
e.Measure(availableSize);
requiredSize.Height += e.DesiredSize.Height;
requiredSize.Width = Math.Max(requiredSize.Width, e.DesiredSize.Width);
}
return new Size(
Math.Min(availableSize.Width, requiredSize.Width),
Math.Min(availableSize.Height, requiredSize.Height));
}
protected override Size ArrangeOverride(Size finalSize)
{
double requiredHeight = 0;
foreach (UIElement e in InternalChildren)
{
requiredHeight += e.DesiredSize.Height;
}
double scale = 1;
if (requiredHeight > finalSize.Height)
{
scale = finalSize.Height / requiredHeight;
}
double y = 0;
foreach (UIElement e in InternalChildren)
{
double height = e.DesiredSize.Height * scale;
e.Arrange(new Rect(0, y, finalSize.Width, height));
y += height;
}
return finalSize;
}
}
我的测试XAML如下所示:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
<x:Array x:Key="Items" Type="{x:Type sys:String}">
<sys:String>Item1</sys:String>
<sys:String>Item2</sys:String>
<sys:String>Item3</sys:String>
<sys:String>Item4</sys:String>
</x:Array>
</Window.Resources>
<local:MyStackPanel>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
</local:MyStackPanel>
</Window>
但是输出看起来像这样:
如您所见,项目正在剪辑-列表框应显示滚动条。子项目不遵守安排通行证中给他们的尺寸。
从我的调查看来,在中,您不能在安排传递中为控件提供比在度量传递中传递给小的控件。
但是,我无法执行此操作,因为我需要测量通行证的结果才能知道在整理通行证中要给 child 多少尺寸。
好像是鸡和蛋的情况。 WPF中的布局是否损坏?当然,测验合格应该就是测验合格?
最佳答案
您遇到的问题是,您将所有可用空间传递给每个 child 到其Measure
调用(e.Measure(availableSize)
)。但是您只需要传递实际要分配给他们的那部分空间。像这样:
protected override Size MeasureOverride(Size availableSize)
{
Size requiredSize = new Size();
var itemAvailableSize = new Size(availableSize.Width, availableSize.Height / InternalChildren.Count);
foreach (UIElement e in InternalChildren)
{
e.Measure(itemAvailableSize);
requiredSize.Height += e.DesiredSize.Height;
requiredSize.Width = Math.Max(requiredSize.Width, e.DesiredSize.Width);
}
return new Size(
Math.Min(availableSize.Width, requiredSize.Width),
Math.Min(availableSize.Height, requiredSize.Height));
}
更新:
如果不容易根据
availableSize
计算计划提供给每个单独项目的大小,并且取决于其他所需的大小,则可以对将double.PositiveInfinity
作为Height
传递的所有项目进行第一轮测量。之后,您将知道每个项目要多大,然后可以计算出实际要为每个项目分配多少空间。然后,您需要再次使用计算出的空间调用Measure。这是一个例子:
protected override Size MeasureOverride(Size availableSize)
{
var requiredSize = new Size();
double allItemsHeight = 0;
foreach (UIElement e in InternalChildren)
{
e.Measure(new Size(availableSize.Width, double.PositiveInfinity));
allItemsHeight += e.DesiredSize.Height;
}
double scale = 1;
if (allItemsHeight > availableSize.Height)
{
scale = availableSize.Height / allItemsHeight;
}
foreach (UIElement e in InternalChildren)
{
double height = e.DesiredSize.Height * scale;
e.Measure(new Size(availableSize.Width, height));
requiredSize.Height += e.DesiredSize.Height;
requiredSize.Width = Math.Max(requiredSize.Width, e.DesiredSize.Width);
}
return new Size(
Math.Min(availableSize.Width, requiredSize.Width),
Math.Min(availableSize.Height, requiredSize.Height));
}