为什么测量不考虑最小宽度和高度

时间:2017-12-30 13:22:01

标签: c# wpf

为何措施不尊重MinWidthMinHeight

FrameworkElement fe = new FrameworkElement() { MinWidth = 100, MinHeight = 100 };
fe.Measure(new Size(50,50));
// Why: fe.DesireSize == {50; 50} and not {100; 100}?.

这背后的理由是什么? MeasureOverride是否应始终返回最大可用尺寸?使用无限大小进行测量会导致{100; 100}

虽然我问可能与定义UIElement方法的Measure有关。我猜FrameworkElement无法通过MinWidthMinHeight来削弱措施后期条件。

编辑:再次看之后,对Measure的调用似乎已经反映了子项上(最小/最大)宽度和高度的约束。父级正在限制其子级遵守指定的/最小/最大值。

在我的情况下:fe.Measure(new Size(fe.MinWidth, fe.MinHeight));本来是正确的电话。

我的用例的一些背景知识。我需要一个用于物品控制的面板。物品最多获得“剩余空间”的百分比,但我想尊重物品的最小宽度或设定宽度

例如:

首先取2个文本框,其中一个内容超过100长度,第二个文本框的MinWidth为100.当MyPanel小于200像素时,第一个文本框应为第二个文本框留下100个像素。如何确定第一个文本框的可用大小?当说面板的宽度为150时。第一个文本框不应该取75而是50.它几乎(如果第二个文本框文本为空)就像一个网格,其中第一个列宽为*,第二个列宽为auto。但现在让网格决定第二个列宽是自动的,因为文本框具有非零的最小宽度(或设置宽度)。如果第二个文本框有很多文本,网格的行为应该像第二个列宽也是*。

到目前为止,我有:

public class MyPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        double desiredWidth = 0;
        foreach (UIElement child in InternalChildren)
        {
            child.Measure(availableSize);
            desiredWidth += child.DesiredSize.Width;
        }

        Size result = new Size();
        int i = InternalChildren.Count;
        foreach (UIElement child in InternalChildren)
        {
            double availableWidthMinimum = Math.Max(0, (availableSize.Width - result.Width) / i);
            desiredWidth -= child.DesiredSize.Width;
            double availableWidthMaximum = Math.Max(0, (availableSize.Width - result.Width - desiredWidth));
            double availableWidth = Math.Max(availableWidthMinimum, availableWidthMaximum);
            child.Measure(new Size(availableWidth, availableSize.Height));
            result.Width += child.DesiredSize.Width;
            result.Height = Math.Max(result.Height, child.DesiredSize.Height);
            i--;
        }
        return result;
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        Point start = new Point(0, 0);
        foreach (UIElement child in InternalChildren)
        {
            child.Arrange(new Rect(start, child.DesiredSize));
            start.X += child.DesiredSize.Width;
        }
        return arrangeSize;
    }
}

XAML:

<local:MyPanel Margin="100,0,0,0">
    <TextBox MinWidth="100" Margin="10">MinWidth=100</TextBox>
    <TextBox MinWidth="0" Margin="10">MinWidth=000</TextBox>
    <TextBox MinWidth="0" Margin="10">Small</TextBox>
    <TextBox MinWidth="0" Margin="10">MinWidth=000</TextBox>
</local:MyPanel>

这种工作方式,但在调整包含面板的窗口大小时,第一个文本框应保持至少100。你会注意到第4个文本框仍然比第二个文本框大一些。 (两者应首先变得与第3个一样小:[Min200][Large][Small][Large] - &gt; [Min200][Small][Small][Small]

1 个答案:

答案 0 :(得分:0)

DesiredSize反映了元素相对于其容器的实际/潜在可见大小。

来自https://msdn.microsoft.com/en-us/library/system.windows.uielement(v=vs.110).aspx

  

<强> DesiredSize

     

获取此元素在布局过程的度量传递期间计算的大小。

Measure计算的方式解释为here

  

public void Measure(Size availableSize)

     

<强>参数

     

availableSize

     

输入:System.Windows.Size

     

父元素可以分配子元素的可用空间。子元素可以请求比可用空间更大的空间;如果在当前元素的内容模型中可以滚动,则可以提供所提供的大小。

(强调我的。)

我已经汇总了一些证明这一点的例子:

元素为按钮内容

MainWindow.xaml

<Window x:Class="DesiredSizeTesting.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="300" Width="300">
    <Grid>
        <Button Click="Button_Click" Width="50" Height="50" Padding="0" BorderThickness="0">
            <Button.Content>
                <Label x:Name="lbl" MinWidth="100" MinHeight="100" />
            </Button.Content>
        </Button>
    </Grid>
</Window>

在MainWindow.xaml.cs

private void Button_Click(object sender, RoutedEventArgs e)
{
    Debug.WriteLine(lbl.DesiredSize);
    Debug.WriteLine(lbl.ActualWidth);
    Debug.WriteLine(lbl.ActualHeight);
}

点击输出

50,50
100
100

此示例类似于将new Size(50,50)传递给Measure

切换到StackPanel

<Grid>
    <StackPanel Width="50" Height="50" Orientation="Vertical">
        <Label x:Name="lbl" MinWidth="100" MinHeight="100" />
    </StackPanel>
    <Button Click="Button_Click" Width="50" Height="50" Padding="0" BorderThickness="0" VerticalAlignment="Bottom" />
</Grid>

点击输出

50,100
100
100

切换StackPanel方向

<Grid>
    <StackPanel Width="50" Height="50" Orientation="Horizontal">
        <Label x:Name="lbl" MinWidth="100" MinHeight="100" />
    </StackPanel>
    <Button Click="Button_Click" Width="50" Height="50" Padding="0" BorderThickness="0" VerticalAlignment="Bottom" />
</Grid>

点击输出

100,50
100
100

使用ScrollViewer

<Grid>
    <ScrollViewer Width="50" Height="50" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Label x:Name="lbl" MinWidth="100" MinHeight="100" />
    </ScrollViewer>
    <Button Click="Button_Click" Width="50" Height="50" Padding="0" BorderThickness="0" VerticalAlignment="Bottom" />
</Grid>

点击输出

100,100
100
100

<强>更新

关于下面评论中描述的场景,这是非常动态的,但您可以使用转换器来实现:

<Grid x:Name="grd" Width="250">
    <Grid.ColumnDefinitions>
        <ColumnDefinition>
            <ColumnDefinition.Width>
                <MultiBinding Converter="{StaticResource glc}" ConverterParameter="1">
                    <Binding Path="ActualWidth" ElementName="grd" />
                    <Binding Path="MinWidth" ElementName="tb" />
                </MultiBinding>
            </ColumnDefinition.Width>
        </ColumnDefinition>
        <ColumnDefinition>
            <ColumnDefinition.Width>
                <MultiBinding Converter="{StaticResource glc}" ConverterParameter="0">
                    <Binding Path="ActualWidth" ElementName="grd" />
                    <Binding Path="MinWidth" ElementName="tb" />
                </MultiBinding>
            </ColumnDefinition.Width>
        </ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TextBox Grid.Column="0" Text="............................................................................." />
    <TextBox x:Name="tb" Grid.Column="1" MinWidth="100" Text="............................................................................." />
</Grid>

StaticResource“glc”指的是以下转换器:

public class GridLengthConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Count() == 2 && values[0] is double && values[1] is double)
        {
            double gridActualWidth = (double)values[0];
            double tbMinWidth = (double)values[1];

            if (gridActualWidth >= tbMinWidth * 2)
            {
                return new GridLength(1, GridUnitType.Star);
            }
            else
            {
                return parameter.ToString() == "0" ? new GridLength(tbMinWidth) : new GridLength(gridActualWidth - tbMinWidth);
            }
        }

        return new GridLength(1, GridUnitType.Star);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

在运行时,如果需要使用它们,您可以访问所有ActualWidth / ActualHeight属性值。