我正在尝试使我认为是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));
    }
    

    10-08 03:50