我正在尝试制作我认为是WPF中的简单Panel,它具有以下属性:
如果孩子的组合高度低于可用的高度,则所有孩子都会显示在所需的高度。
如果孩子的组合高度大于可用身高,所有孩子的身高都会减少相同的百分比。
我的面板如下所示:
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>
但输出如下:
如您所见,项目是剪辑 - 列表框应显示滚动条。儿童用品不符合安排通行证中给予他们的尺寸。
根据我的调查,似乎你不能给编号传递中的控件提供比测量传递中的控件更小的尺寸。
但是,我不能这样做,因为我需要测量传递的结果才能知道在安排传递中给孩子们的大小。
看起来像鸡蛋和鸡蛋的情况。 WPF中的布局是否已损坏?当然,度量通过应该只是度量传递?
答案 0 :(得分:5)
您的问题是,您将所有可用空间传递给每个孩子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
计算每个项目的大小并且取决于所需的其他项目大小,您可以对所有通过的项目进行第一轮测量{{ 1}}为double.PositiveInfinity
。之后,您将知道每件物品的大小,并且您可以计算出您实际要为每件物品提供多少空间。然后,您需要再次使用计算的空间调用Measure。
以下是一个例子:
Height
答案 1 :(得分:0)
这是一个非常有趣的问题,我认为我不能回答“布局已被打破” - 我确信它不应该只是看着你的例子我无法弄清楚如何“修复”它使用度量/布局传递。
然而,我可以建议以下作为获得(大致)期望效果的替代方案:<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding Items.Count,
RelativeSource={RelativeSource FindAncestor, AncestorType=ItemsControl}}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
<ListBox ItemsSource="{StaticResource Items}"/>
</ItemsControl>
这将为列表中的每个项目分配一定量的空间。如果没有足够的项目来填充所有可用空间,它就不会起作用,但它几乎就在那里。
答案 2 :(得分:-2)
问题是你告诉你的孩子控件他们可以在MeasureOverride中拥有整个可用的大小
e.Measure(availableSize);
您需要限制此处的大小,让他们知道它们会被调整大小。您应该能够在MeasureOverride中实现类似的逻辑。