排列/测量问题 - WPF中的布局是否已损坏?

Gro*_*kys 13 c# wpf layout

我试图制作我认为是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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我的测试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>
Run Code Online (Sandbox Code Playgroud)

但输出看起来像这样:

布局问题

如您所见,项目是剪切 - 列表框应显示滚动条.儿童用品不符合安排通行证中给予他们的尺寸.

从我的调查来看,似乎你不能给安排通道中的控件提供比测量通道中更小的尺寸.

但是,我不能这样做,因为我需要测量传递的结果才能知道在传递通道中给孩子们的大小.

这似乎是鸡和鸡蛋的情况.WPF中的布局是否已损坏?当然,措施通过应该只是一个措施通过?

Pav*_*kov 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));
}
Run Code Online (Sandbox Code Playgroud)

更新:

如果当您打算给各个项目的大小是不容易计算基础上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));
}
Run Code Online (Sandbox Code Playgroud)

  • @Groky - 您可以在“MeasureOverride”中根据需要多次调用“Measure”。因此,您可以对所有通过“double.PositiveInfinity”作为“Height”的项目进行第一轮测量。之后,您将知道每个项目想要有多大,并且可以计算实际要为每个项目提供多少空间。之后,再次使用计算出的空间调用“Measure”。 (2认同)