如何将容器的高度约束到特定内容元素的高度?

时间:2010-06-11 23:21:42

标签: wpf layout size panel element

我正在尝试做一些似乎应该非常简单的事情,但我却看不出如何。我有一个非常简单的布局,一个TextBox旁边有一个图像,类似于在WinForms应用程序中看起来用ErrorProvider装饰的方式。问题是,我希望图像不高于它旁边的TextBox。如果我这样说出来,请说:

<Grid>  
  <Grid.RowDefinitions>
    <RowDefinition Height="Auto"/>
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <TextBox Grid.Row="0" Grid.Column="0" MinWidth="100"/>
  <Image Grid.Row="0" Grid.Column="1" Source="error.png" />
</Grid>

如果图像高于TextBox,则行的大小将调整为图像的高度。如果我使用DockPanel或StackPanel,也会发生这种情况。

天真的解决方案是将Height绑定到TextBox的{​​{1}}。我确定这是错的。但是什么是对的?

修改

以下是我看错了的一个示例:在这两种布局中(均为水平ActualHeight s),StackPanel是唯一的变量:

Two examples showing unscaled icons

您可以看到第一个FontSize被约束到图标的高度,因此在文本下面有一个不必要的底部填充。第二个旁边的图标超出了它旁边的TextBox

实际上,我发现了一种完全不同(并且更好)的方法来处理问题 - 最初我通过更改TextBox上的FontSize来扩展我的布局,但使用{{ 1}}更简单,似乎完美无缺。但即便如此,我仍然觉得很难做到这一点。

2 个答案:

答案 0 :(得分:2)

为TextBox命名,从Image引用TextBox,如下所示。

<TextBox Name="myTextBox" 
         Grid.Row="0" Grid.Column="0" 
         MinWidth="100"/> 
<Image Grid.Row="0" 
       Grid.Column="1" 
       Source="error.png" 
       Height="{Binding ActualHeight, ElementName=myTextBox}"/> 

答案 1 :(得分:1)

您需要一种布局算法,用于测量高度约束等于特定高度所需高度的其他元素。虽然现有的几个Panel实现将根据先前使用的大小减少剩余元素的可用空间,但它们都不会按照您想要的方式设置约束。如果您希望在单个布局中描述的行为通过,则需要编写自己的布局算法。

例如,您可以通过覆盖StackPanel的行为来接近:

public class SizeToFirstChildStackPanel
    : StackPanel
{
    protected override Size MeasureOverride(Size constraint)
    {
        if (Children.Count > 0)
        {
            var firstChild = Children[0];
            firstChild.Measure(constraint);
            if (Orientation == Orientation.Horizontal)
            {
                constraint = new Size(
                    constraint.Width,
                    Math.Min(firstChild.DesiredSize.Height, constraint.Height));
            }
            else
            {
                constraint = new Size(
                    Math.Min(firstChild.DesiredSize.Width, constraint.Width),
                    constraint.Height);
            }
        }
        return base.MeasureOverride(constraint);
    }
}

这会将StackPanel的所有子项的高度约束到第一个子项的所需高度(如果面板垂直定向,则为宽度)。

它仍然不理想,因为第一个孩子将通过基类MeasureOverride第二次测量,第二个测量将有一个约束。这是额外的工作,如果第一个孩子想要变大,会导致奇怪的行为。

要做得对,您需要自己实现整个MeasureOverride方法。我不会在这里这样做,因为它会有很多与问题无关的代码,而且取决于你希望布局的确切运行方式。重要的是首先测量特定元素,并在其他人调用Measure时使用DesiredSize来确定availableSize。