【问题标题】:How do I constrain a container's height to the height of a specific content element?如何将容器的高度限制为特定内容元素的高度?
【发布时间】:2011-03-02 21:28:17
【问题描述】:

我正在尝试做一些看起来应该非常简单的事情,但我不知道该怎么做。我有一个非常简单的布局,一个 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>

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

天真的解决方案是将Height 绑定到TextBoxActualHeight。我确定这是错误的。但是什么是对的?

编辑

以下是我认为错误的示例:在这两种布局中(都是水平的StackPanels),FontSize 是唯一的变量:

您可以看到第一个TextBox 被限制为图标的高度,因此在文本下方有一个不必要的底部填充。第二个旁边的图标与它旁边的TextBox 不成比例。

碰巧,我找到了一种完全不同(而且更好)的方法来解决这个问题 - 最初我是通过更改 Window 上的 FontSize 来缩放我的布局,但使用 ScaleTransform 是一个整体容易得多,似乎工作得很好。但即便如此,我仍然觉得很难做到这一点。

【问题讨论】:

    标签: wpf layout size panel element


    【解决方案1】:

    为您的文本框命名,从图像中引用文本框,如下所示。

    <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}"/> 
    

    【讨论】:

    • 好吧,就像我说的,这是一个幼稚的解决方案,我怀疑它是错误的 - 不是因为它不起作用,而是因为绑定到 ActualHeight 和 ActualWidth 会导致非常明显的性能问题。跨度>
    • 我使用这个想法来保持图像的高度与组合框中的高度文本相同,并取得了很好的效果。
    【解决方案2】:

    您需要一种布局算法来测量其他元素的高度限制,该限制等于特定元素的所需高度。虽然现有的几个 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。

    【讨论】:

      猜你喜欢
      • 2016-01-18
      • 2012-06-07
      • 2018-06-06
      • 2012-07-20
      • 1970-01-01
      • 2021-03-19
      • 1970-01-01
      相关资源
      最近更新 更多