为什么嵌套网格中的星星尺寸不工作?

时间:2022-11-13 10:24:50

Consider the following XAML:

考虑下面的XAML:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <Grid Grid.Column="1">
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Button Content="B" Margin="5"/>
    <Button Content="Button" Grid.Column="1" Margin="5"/>
  </Grid>
</Grid>

In the above, all ColumnDefinition values except one use the default value for Width, which is "*", i.e. "star sizing". The one exception is the column containing the nested Grid control, which is set to "Auto".

在上面的示例中,除一个外的所有列定义值都使用宽度的默认值,即“*”,即“明星分级”。一个例外是包含嵌套网格控件的列,它被设置为“Auto”。

The way I expect this to work is as follows:

我期望它的工作方式如下:

  • The outer Grid sizes the second column according to the needs of its content, and then assigns the remaining width of the control to the first column.
  • 外部网格根据内容的需要对第二列进行大小调整,然后将控件的其余宽度分配给第一列。
  • The inner Grid distributes its available space evenly to the two columns. After all, they both are set to use star sizing, and star sizing is supposed to set the GridLength property (width, in this case) to a weighted distribution of available space. The minimum layout size for this inner Grid (needed for the outer Grid to compute the width of its second column) is the sum of evenly-distributed-width, star-sized columns (i.e. in this case, two times the width of the column with the widest content).
  • 内部网格将其可用空间均匀地分配给这两列。毕竟,它们都被设置为使用星型大小,而星型大小应该将GridLength属性(在本例中为宽度)设置为可用空间的加权分布。这个内部网格的最小布局大小(外部网格计算其第二列宽度所需)是均匀分布的、星形的列的总和(在本例中,是内容最宽的列的两倍)。

But instead, the column widths for the nested grid are set according to the computed minimum size of each button, with no apparent weighted relationship between the two star-sized columns (gridlines are shown for clarity):

相反,嵌套网格的列宽是根据每个按钮计算的最小大小来设置的,这两个星形的列之间没有明显的加权关系(网格线是为了清晰显示):

为什么嵌套网格中的星星尺寸不工作?

It works as expected if I don't have the outer grid, i.e. just make the inner grid the only grid in the window:

如果我没有外部网格,它会像预期的那样工作,也就是说,让内部网格成为窗口中唯一的网格:

为什么嵌套网格中的星星尺寸不工作?

The two columns are forced to be the same size, and then of course the left-hand button is stretched to fit the size of its containing cell (which is what I want…the end goal is for those two buttons to have the same width, with the grid columns providing the layout to accomplish that).

两列*是相同的大小,当然左边按钮包含细胞伸展以适应规模(这就是我想要的……最终的目标是对于那些两个按钮有相同的宽度,与网格列提供的布局完成)。


In this particular example, I can use UniformGrid as a work-around, to force even distribution of column widths. This is how I want it actually to look (UniformGrid doesn't have a ShowGridLines property, so you just have to imagine the imaginary line between the two right-most buttons):

在这个特殊的例子中,我可以使用均布网格作为一个工作区,来强制列宽的均匀分布。这就是我想让它看起来的样子(UniformGrid没有ShowGridLines属性,所以你只需想象两个最右边的按钮之间的虚线):

为什么嵌套网格中的星星尺寸不工作?

But I really would like to understand more generally how to accomplish this, so that in more complex scenarios I would be able to use star-sizing in a nested Grid control.

但我真的想更一般地了解如何实现这一点,以便在更复杂的场景中,我能够在嵌套网格控件中使用星型大小。


It seems that somehow, being contained within the cell of another Grid control is changing the way that star sizing is computed for the inner Grid control (or preventing star sizing from having any effect at all). But why should this be? Am I missing (yet again) some esoteric layout rule of WPF that explains this as "by design" behavior? Or is this simply a bug in the framework?

似乎在某种程度上,包含在另一个网格控件的单元中,改变了对内部网格控件计算星型大小的方式(或者阻止星型大小产生任何影响)。但这是为什么呢?我是否又错过了WPF的一些深奥的布局规则,将其解释为“通过设计”行为?或者这只是框架中的一个bug ?


Update:

更新:

I understand Ben's answer to mean that star-sizing should be distributing only the left-over space after the minimum sizes for each column has been accounted for. But that is not what one sees in other scenarios.

我理解Ben的回答,即星型尺寸应该只分布在每个列的最小尺寸之后的剩余空间。但这不是你在其他场景中看到的。

For example, if the column containing the inner grid has been sized explicitly, then using star-sizing for the inner grid's columns results in the columns being sized evenly, just as I'd expect.

例如,如果包含内部网格的列被显式地调整了大小,那么对内部网格的列使用星型调整将导致列被均匀地调整大小,正如我所期望的那样。

I.e. this XAML:

即这XAML:

<Grid ShowGridLines="True">
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <!--
    <ColumnDefinition Width="Auto"/>
    -->
    <ColumnDefinition Width="60"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <Grid Grid.Column="1" ShowGridLines="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Button Content="B" Margin="5"/>
    <Button Content="Button" Grid.Column="1" Margin="5"/>
  </Grid>
</Grid>

produces this output:

产生该输出:

为什么嵌套网格中的星星尺寸不工作?

In other words, at worst, I'd expect WPF to first calculate the minimum size of the inner grid without considering the star-sizing (e.g. if the short button takes 10 pixels and the long button takes 70, then the total width would be 80), and then still distribute evenly the column widths (i.e. in the 10/70 example, each column would wind up with 40 pixels, truncated the longer button, similar to the above image).

换句话说,在最坏的情况下,我希望WPF首先计算网格内的最小大小而不考虑star-sizing(例如,如果短按钮需要10像素和长按钮需要70,然后总宽度是80),然后仍然均匀分布列宽(即在10/70的例子中,每一列40像素,将截断按钮的时间越长,类似于上图)。

Why should star-sizing sometimes evenly distribute the widths across columns and sometimes not?

为什么星型尺寸有时会均匀地分布在各列之间,有时则不会?


Update #2:

更新2:

Here is a simple example that shows clearly and dramatically how WPF treats star-sizing differently depending on whether it's the one to compute the Grid width or you are:

这里有一个简单的例子,它清楚而显著地展示了WPF是如何根据它是计算网格宽度的人还是你是谁来区别对待恒星大小的:

<Window x:Class="TestGridLayout2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        SizeToContent="WidthAndHeight"
        Title="MainWindow">
  <Window.Resources>
    <Style TargetType="Border">
      <Setter Property="BorderBrush" Value="Black"/>
      <Setter Property="BorderThickness" Value="1"/>
    </Style>
    <Style TargetType="TextBlock">
      <Setter Property="FontSize" Value="24"/>
    </Style>
  </Window.Resources>
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="3*"/>
    </Grid.ColumnDefinitions>

    <Border Grid.Column="0"/>
    <Border Grid.Column="1"/>

    <StackPanel>
      <TextBlock Text="Some text -- one"/>
      <TextBlock Text="Some text -- two"/>
      <TextBlock Text="Some text -- three"/>
    </StackPanel>
    <StackPanel Grid.Column="1">
      <TextBlock Text="one"/>
      <TextBlock Text="two"/>
      <TextBlock Text="three"/>
    </StackPanel>
  </Grid>
</Window>

When you run the program, you see this:

当你运行这个程序时,你会看到:

为什么嵌套网格中的星星尺寸不工作?

WPF has ignored star-sizing, setting each column width to its minimum. If you simply click on the window border, as if to resize the window (you don't even have to actually drag the border anywhere), the Grid layout gets redone, and you get this:

WPF忽略了星型大小,将每个列的宽度设置为最小。如果你只是点击窗口边框,就好像要调整窗口大小(你甚至不需要把边框拖到任何地方),网格布局就会重新完成,你会得到:

为什么嵌套网格中的星星尺寸不工作?

At this point, the star-sizing gets applied (as I'd expect) and the columns are proportioned according to the XAML declarations.

此时,应用星型大小(正如我所期望的那样)并根据XAML声明按比例分配列。

3 个解决方案

#1


4  

I would agree that Ben's answer strongly hints at what might be going on underneath the covers here:

我同意Ben的回答强烈暗示了在书中隐藏着什么:

As Ben points out, WPF is ignoring the star-sizing for the purposes of computing the inner Grid object's minimum width (perhaps reasonably…I think there's room for honest debate, but clearly that's one possible and legitimate design).

正如Ben所指出的,WPF为了计算内部网格对象的最小宽度而忽略了星型大小(也许是合理的……我认为确实有讨论的空间,但显然这是一种可能的、合法的设计)。

What's not clear (and which Ben does not answer) is why this should then imply that star-sizing is also ignored when it comes time to calculate the column widths within that inner Grid. Since when a width is imposed externally, proportional widths will cause content to be truncated if necessary to preserve those proportions, why does the same thing not happen when the width is computed automatically based on the minimum required sizes of the content.

有什么不清楚(本不回答)的原因是,在计算内部网格内的列宽度时,这就暗示了星号也被忽略了。由于在外部施加宽度时,如果需要保持这些比例,比例宽度将导致内容被截断,为什么在根据内容的最小所需大小自动计算宽度时不会发生同样的事情呢?

I.e. I'm still looking for the answer to my question.

我还在寻找我问题的答案。


In the meantime, IMHO useful answers include work-arounds to the issue. While not actual answers to my question per se, they are clearly helpful to anyone who may run across the issue. So I'm writing this answer to consolidate all the known work-arounds (for better or worse, one big "feature" of WPF is that there always seems to be at least a few different ways to accomplish the same result :) ).

与此同时,不甚有用的答案包括解决这个问题的方法。虽然这不是我的问题的实际答案,但它们显然对任何可能遇到问题的人都有帮助。因此,我编写这个答案是为了巩固所有已知的工作方法(无论如何,WPF的一个重要“特性”是,似乎总有至少几种不同的方法可以实现相同的结果:)。


Workaround #1:

解决方法1:

Use UniformGrid instead of Grid for the inner grid object. This object does not have all the same features as Grid and of course doesn't allow for any columns to be of different width. So it may not be useful in all scenarios. But it does easily address the simple one here:

内部网格对象使用均布格替代网格。这个对象不具有与Grid相同的所有特性,当然也不允许任何列具有不同的宽度。所以它可能不是在所有的情况下都有用。但它确实很容易解决这个简单的问题:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <UniformGrid Rows="1" Columns="2" Grid.Column="1">
    <Button Content="B" Margin="5"/>
    <Button Content="Button" Grid.Column="1" Margin="5"/>
  </UniformGrid>
</Grid>


Workaround #2:

解决方案2:

Bind the MinWidth property of the smaller content object (e.g. here, the first Button in the grid) to the ActualWidth property of the larger one. This of course requires knowing which object has the largest width. In localization scenarios, that could be problematic, as the XAML would have to be localized in addition to the text resources. But that is sometimes necessary anyway, so… :)

将较小内容对象的MinWidth属性(例如,网格中的第一个按钮)绑定到较大内容对象的实际宽度属性。这当然需要知道哪个对象的宽度最大。在本地化场景中,这可能会有问题,因为XAML必须本地化,除了文本资源之外。但这有时是必要的,所以……

That would look something like this (and is essentially what answerer dub stylee provided as an answer here):

这看起来就像这样(基本上就是回答者dub stylee提供的答案):

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <Grid Grid.Column="1">
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Button Content="B" Margin="5"
            MinWidth="{Binding ElementName=button2, Path=ActualWidth}"/>
    <Button x:Name="button2" Content="Button" Grid.Column="1" Margin="5"/>
  </Grid>
</Grid>

A variation (which for brevity I won't include here) would be to use a MultiBinding that takes all of the relevant controls as input and returns the largest MinWidth of the collection. Of course, then this binding would be used for the Width of each ColumnDefinition, so that all the columns were explicitly set to the largest MinWidth.

一种变体(为了简单起见,我不在这里介绍)是使用多绑定,该多绑定将所有相关控件作为输入并返回集合的最大最小宽度。当然,这个绑定将用于每个列定义的宽度,因此所有列都显式地设置为最大的最小宽度。

There are other variations on the binding scenario as well, depending on which widths you want to use and/or set. None of these are ideal, not just because of the potential localization issues, but also because it embeds more explicit relationships into the XAML. But in many scenarios, it will work perfectly.

绑定场景还存在其他变化,这取决于您希望使用和/或设置的宽度。但在许多情况下,它会完美地工作。


Workaround #3:

方法# 3:

By using the SharedSizeGroup property of the ColumnDefinition values, it is possible to explicitly force a group of columns to have the same width. In this approach, the inner Grid object's minimum width is then computed on that basis, and of course the widths wind up the same too.

通过使用ColumnDefinition值的SharedSizeGroup属性,可以显式强制一组列具有相同的宽度。在这种方法中,内部网格对象的最小宽度在此基础上进行计算,当然,宽度最终也是相同的。

For example:

例如:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <Grid Grid.Column="1" IsSharedSizeScope="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition SharedSizeGroup="buttonWidthGroup"/>
      <ColumnDefinition SharedSizeGroup="buttonWidthGroup"/>
    </Grid.ColumnDefinitions>
    <Button Content="B" Margin="5"/>
    <Button Content="Button" Grid.Column="1" Margin="5"/>
  </Grid>
</Grid>

This approach allows one to use Grid, and so get all the normal features of that object, while addressing the specific behavior of concern. I would expect it not to interfere with any other legitimate use of SharedSizeGroup.

这种方法允许使用网格,从而获得该对象的所有正常特性,同时处理关注的特定行为。我希望它不会干扰SharedSizeGroup的其他合法使用。

However, it does imply "Auto" sizing, and precludes "*" (star) sizing. In this particular scenario, that's not a problem, but as in the case of the UniformGrid work-around, it does limit one's options when trying to combine this with other sizing. E.g. having a third column use "Auto" and wanting the SharedSizeGroup columns to take the remaining space of the Grid.

但是,它确实意味着“自动”分级,并且排除了“*”(星形)分级。在这个特定的场景中,这不是一个问题,但是在使用均格网格时,在尝试将它与其他分级结合时,它确实限制了一个人的选项。例如,有第三列使用“Auto”,并希望SharedSizeGroup列占用网格的剩余空间。

Still, this would work in many scenarios without any trouble at all.

不过,这在很多情况下都能正常工作。


Workaround #4:

方法# 4:

I wound up revisiting this question because I ran into a variation on the theme. In this case, I am dealing with a situation where I want to have different proportions for the columns that are being sized. All of the above workarounds assume equal-sized columns. But I want (for example) one column to have 25% of the space, and another column to have 75% of the space. As before, I want the total size of the Grid to accommodate the minimum width required for all of the columns.

我又回到了这个问题,因为我在这个主题上遇到了一个变奏。在这种情况下,我要处理的情况是我想要得到不同比例的列的大小。所有上述工作区都假定为等大小的列。但是我希望(例如)一列有25%的空间,另一列有75%的空间。与前面一样,我希望网格的总大小能够满足所有列所需的最小宽度。

This workaround involves simply explicitly doing myself the computation I feel that WPF ought to be doing. I.e. taking the minimum widths of the content of each column, along with the specified proportional sizes, compute the actual width of the Grid control.

这个解决方案只需要显式地为自己做我认为WPF应该做的计算。即取每一列内容的最小宽度,以及指定的比例大小,计算网格控件的实际宽度。

Here is a method that will do that:

下面是一种方法:

private static double ComputeGridSizeForStarWidths(Grid grid)
{
    double maxTargetWidth = double.MinValue, otherWidth = 0;
    double starTotal = grid.ColumnDefinitions
        .Where(d => d.Width.IsStar).Sum(d => d.Width.Value);

    foreach (ColumnDefinition definition in grid.ColumnDefinitions)
    {
        if (!definition.Width.IsStar)
        {
            otherWidth += definition.ActualWidth;
            continue;
        }

        double targetWidth = definition.ActualWidth / (definition.Width.Value / starTotal);

        if (maxTargetWidth < targetWidth)
        {
            maxTargetWidth = targetWidth;
        }
    }

    return otherWidth + maxTargetWidth;
}

This code finds the smallest width that can still accommodate every star-sized column at that columns minimum width and proportional sizing, along with the remaining columns that are not using star-sizing.

这段代码找到了最小的宽度,该宽度仍然适用于该列上的每个星型列,最小的宽度和比例大小,以及不使用星型大小的其他列。

You can call this method at an appropriate time (e.g. in the Grid.Loaded event handler), and then assign its return value to the Grid.Width property to force the width to the right size to accommodate the minimum required widths for all columns while maintain the specified proportions.

您可以在适当的时候调用这个方法(例如在网格中)。加载事件处理程序),然后将其返回值分配给网格。宽度属性将宽度强制为正确的大小,以适应所有列所需的最小宽度,同时保持指定的比例。

A similar method would do the same thing for row heights.

对于行高度,类似的方法也可以这样做。


Workaround #5:

方法# 5:

I guess it bears pointing out: one can simply specify the Grid size explicitly. This only works for content where the size is known in advance, but again, in many scenarios this would be fine. (In other scenarios, it will truncate content, because even if the size specified is too small, when it's explicit, WPF goes ahead and applies the star-sizing proportions).

我想它需要指出的是:可以简单地显式地指定网格大小。这只适用于预先知道大小的内容,但在许多情况下,这是可以的。(在其他场景中,它将截断内容,因为即使指定的大小太小,当它是显式的时,WPF将继续执行并应用星型大小比例)。


I encourage others to add additional work-arounds to this answer, if they are aware of good work-arounds that are materially different from those already shown. Alternatively, feel free to post another answer with your work-around, and I will (at my earliest convenience :) ) add it here myself.

我鼓励其他人在这个答案中增加额外的工作,如果他们知道好的变通方法和已经显示出来的方法有很大的不同。或者,也可以在你的工作中发布另一个答案,我将(在我最方便的时候:)自己在这里添加它。

#2


2  

This is a bit of a workaround and doesn't explain the cause of the behavior you are describing, but it achieves what you are looking for:

这只是一个变通方法,并没有解释你所描述的行为的原因,但它实现了你想要的:

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Button HorizontalAlignment="Left" Content="Button" />
    <Grid Grid.Column="1"
          ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0"
                Margin="5"
                MinWidth="{Binding ElementName=Button2, Path=ActualWidth}"
                Content="B"
                x:Name="Button1" />
        <Button Grid.Column="1"
                Margin="5"
                Content="Button"
                x:Name="Button2" />
    </Grid>
</Grid>

为什么嵌套网格中的星星尺寸不工作?

I have to assume that an Auto column width is calculated based on the minimum width of the child controls. I wasn't able to confirm or disprove this looking through any documentation, but it is probably expected behavior. In order to force the Button controls to take up equal size, you can just bind the MinWidth property to the ActualWidth of the other Button. In this example, I only bound the Button1.MinWidth to the Button2.ActualWidth to illustrate your desired behavior.

我必须假设自动列宽度是基于子控件的最小宽度计算的。我无法通过任何文档来证实或反驳这一点,但这可能是预期的行为。为了使按钮控件具有相同的大小,只需将MinWidth属性绑定到另一个按钮的实际宽度。在这个例子中,我只绑定了Button1。MinWidth Button2。实际宽度来说明你想要的行为。

Also, please ignore the Button.Height, I didn't bother to set them different than the default.

另外,请忽略按钮。高度,我没有设置它们与默认值不同。

#3


1  

From the documentation (Modern apps) (WPF):

来自文档(现代应用)(WPF):

starSizing A convention by which you can size rows or columns to take the remaining available space in a Grid.

按约定对行或列进行大小调整,以获取网格中剩余的可用空间。

It doesn't cause more minimum space to be requested, it affects distribution of space in excess of the minimum.

它不会导致需要更多的最小空间,它会影响超过最小空间的分布。

#1


4  

I would agree that Ben's answer strongly hints at what might be going on underneath the covers here:

我同意Ben的回答强烈暗示了在书中隐藏着什么:

As Ben points out, WPF is ignoring the star-sizing for the purposes of computing the inner Grid object's minimum width (perhaps reasonably…I think there's room for honest debate, but clearly that's one possible and legitimate design).

正如Ben所指出的,WPF为了计算内部网格对象的最小宽度而忽略了星型大小(也许是合理的……我认为确实有讨论的空间,但显然这是一种可能的、合法的设计)。

What's not clear (and which Ben does not answer) is why this should then imply that star-sizing is also ignored when it comes time to calculate the column widths within that inner Grid. Since when a width is imposed externally, proportional widths will cause content to be truncated if necessary to preserve those proportions, why does the same thing not happen when the width is computed automatically based on the minimum required sizes of the content.

有什么不清楚(本不回答)的原因是,在计算内部网格内的列宽度时,这就暗示了星号也被忽略了。由于在外部施加宽度时,如果需要保持这些比例,比例宽度将导致内容被截断,为什么在根据内容的最小所需大小自动计算宽度时不会发生同样的事情呢?

I.e. I'm still looking for the answer to my question.

我还在寻找我问题的答案。


In the meantime, IMHO useful answers include work-arounds to the issue. While not actual answers to my question per se, they are clearly helpful to anyone who may run across the issue. So I'm writing this answer to consolidate all the known work-arounds (for better or worse, one big "feature" of WPF is that there always seems to be at least a few different ways to accomplish the same result :) ).

与此同时,不甚有用的答案包括解决这个问题的方法。虽然这不是我的问题的实际答案,但它们显然对任何可能遇到问题的人都有帮助。因此,我编写这个答案是为了巩固所有已知的工作方法(无论如何,WPF的一个重要“特性”是,似乎总有至少几种不同的方法可以实现相同的结果:)。


Workaround #1:

解决方法1:

Use UniformGrid instead of Grid for the inner grid object. This object does not have all the same features as Grid and of course doesn't allow for any columns to be of different width. So it may not be useful in all scenarios. But it does easily address the simple one here:

内部网格对象使用均布格替代网格。这个对象不具有与Grid相同的所有特性,当然也不允许任何列具有不同的宽度。所以它可能不是在所有的情况下都有用。但它确实很容易解决这个简单的问题:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <UniformGrid Rows="1" Columns="2" Grid.Column="1">
    <Button Content="B" Margin="5"/>
    <Button Content="Button" Grid.Column="1" Margin="5"/>
  </UniformGrid>
</Grid>


Workaround #2:

解决方案2:

Bind the MinWidth property of the smaller content object (e.g. here, the first Button in the grid) to the ActualWidth property of the larger one. This of course requires knowing which object has the largest width. In localization scenarios, that could be problematic, as the XAML would have to be localized in addition to the text resources. But that is sometimes necessary anyway, so… :)

将较小内容对象的MinWidth属性(例如,网格中的第一个按钮)绑定到较大内容对象的实际宽度属性。这当然需要知道哪个对象的宽度最大。在本地化场景中,这可能会有问题,因为XAML必须本地化,除了文本资源之外。但这有时是必要的,所以……

That would look something like this (and is essentially what answerer dub stylee provided as an answer here):

这看起来就像这样(基本上就是回答者dub stylee提供的答案):

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <Grid Grid.Column="1">
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Button Content="B" Margin="5"
            MinWidth="{Binding ElementName=button2, Path=ActualWidth}"/>
    <Button x:Name="button2" Content="Button" Grid.Column="1" Margin="5"/>
  </Grid>
</Grid>

A variation (which for brevity I won't include here) would be to use a MultiBinding that takes all of the relevant controls as input and returns the largest MinWidth of the collection. Of course, then this binding would be used for the Width of each ColumnDefinition, so that all the columns were explicitly set to the largest MinWidth.

一种变体(为了简单起见,我不在这里介绍)是使用多绑定,该多绑定将所有相关控件作为输入并返回集合的最大最小宽度。当然,这个绑定将用于每个列定义的宽度,因此所有列都显式地设置为最大的最小宽度。

There are other variations on the binding scenario as well, depending on which widths you want to use and/or set. None of these are ideal, not just because of the potential localization issues, but also because it embeds more explicit relationships into the XAML. But in many scenarios, it will work perfectly.

绑定场景还存在其他变化,这取决于您希望使用和/或设置的宽度。但在许多情况下,它会完美地工作。


Workaround #3:

方法# 3:

By using the SharedSizeGroup property of the ColumnDefinition values, it is possible to explicitly force a group of columns to have the same width. In this approach, the inner Grid object's minimum width is then computed on that basis, and of course the widths wind up the same too.

通过使用ColumnDefinition值的SharedSizeGroup属性,可以显式强制一组列具有相同的宽度。在这种方法中,内部网格对象的最小宽度在此基础上进行计算,当然,宽度最终也是相同的。

For example:

例如:

<Grid>
  <Grid.ColumnDefinitions>
    <ColumnDefinition/>
    <ColumnDefinition Width="Auto"/>
  </Grid.ColumnDefinitions>
  <Button Content="Button" HorizontalAlignment="Left"/>
  <Grid Grid.Column="1" IsSharedSizeScope="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition SharedSizeGroup="buttonWidthGroup"/>
      <ColumnDefinition SharedSizeGroup="buttonWidthGroup"/>
    </Grid.ColumnDefinitions>
    <Button Content="B" Margin="5"/>
    <Button Content="Button" Grid.Column="1" Margin="5"/>
  </Grid>
</Grid>

This approach allows one to use Grid, and so get all the normal features of that object, while addressing the specific behavior of concern. I would expect it not to interfere with any other legitimate use of SharedSizeGroup.

这种方法允许使用网格,从而获得该对象的所有正常特性,同时处理关注的特定行为。我希望它不会干扰SharedSizeGroup的其他合法使用。

However, it does imply "Auto" sizing, and precludes "*" (star) sizing. In this particular scenario, that's not a problem, but as in the case of the UniformGrid work-around, it does limit one's options when trying to combine this with other sizing. E.g. having a third column use "Auto" and wanting the SharedSizeGroup columns to take the remaining space of the Grid.

但是,它确实意味着“自动”分级,并且排除了“*”(星形)分级。在这个特定的场景中,这不是一个问题,但是在使用均格网格时,在尝试将它与其他分级结合时,它确实限制了一个人的选项。例如,有第三列使用“Auto”,并希望SharedSizeGroup列占用网格的剩余空间。

Still, this would work in many scenarios without any trouble at all.

不过,这在很多情况下都能正常工作。


Workaround #4:

方法# 4:

I wound up revisiting this question because I ran into a variation on the theme. In this case, I am dealing with a situation where I want to have different proportions for the columns that are being sized. All of the above workarounds assume equal-sized columns. But I want (for example) one column to have 25% of the space, and another column to have 75% of the space. As before, I want the total size of the Grid to accommodate the minimum width required for all of the columns.

我又回到了这个问题,因为我在这个主题上遇到了一个变奏。在这种情况下,我要处理的情况是我想要得到不同比例的列的大小。所有上述工作区都假定为等大小的列。但是我希望(例如)一列有25%的空间,另一列有75%的空间。与前面一样,我希望网格的总大小能够满足所有列所需的最小宽度。

This workaround involves simply explicitly doing myself the computation I feel that WPF ought to be doing. I.e. taking the minimum widths of the content of each column, along with the specified proportional sizes, compute the actual width of the Grid control.

这个解决方案只需要显式地为自己做我认为WPF应该做的计算。即取每一列内容的最小宽度,以及指定的比例大小,计算网格控件的实际宽度。

Here is a method that will do that:

下面是一种方法:

private static double ComputeGridSizeForStarWidths(Grid grid)
{
    double maxTargetWidth = double.MinValue, otherWidth = 0;
    double starTotal = grid.ColumnDefinitions
        .Where(d => d.Width.IsStar).Sum(d => d.Width.Value);

    foreach (ColumnDefinition definition in grid.ColumnDefinitions)
    {
        if (!definition.Width.IsStar)
        {
            otherWidth += definition.ActualWidth;
            continue;
        }

        double targetWidth = definition.ActualWidth / (definition.Width.Value / starTotal);

        if (maxTargetWidth < targetWidth)
        {
            maxTargetWidth = targetWidth;
        }
    }

    return otherWidth + maxTargetWidth;
}

This code finds the smallest width that can still accommodate every star-sized column at that columns minimum width and proportional sizing, along with the remaining columns that are not using star-sizing.

这段代码找到了最小的宽度,该宽度仍然适用于该列上的每个星型列,最小的宽度和比例大小,以及不使用星型大小的其他列。

You can call this method at an appropriate time (e.g. in the Grid.Loaded event handler), and then assign its return value to the Grid.Width property to force the width to the right size to accommodate the minimum required widths for all columns while maintain the specified proportions.

您可以在适当的时候调用这个方法(例如在网格中)。加载事件处理程序),然后将其返回值分配给网格。宽度属性将宽度强制为正确的大小,以适应所有列所需的最小宽度,同时保持指定的比例。

A similar method would do the same thing for row heights.

对于行高度,类似的方法也可以这样做。


Workaround #5:

方法# 5:

I guess it bears pointing out: one can simply specify the Grid size explicitly. This only works for content where the size is known in advance, but again, in many scenarios this would be fine. (In other scenarios, it will truncate content, because even if the size specified is too small, when it's explicit, WPF goes ahead and applies the star-sizing proportions).

我想它需要指出的是:可以简单地显式地指定网格大小。这只适用于预先知道大小的内容,但在许多情况下,这是可以的。(在其他场景中,它将截断内容,因为即使指定的大小太小,当它是显式的时,WPF将继续执行并应用星型大小比例)。


I encourage others to add additional work-arounds to this answer, if they are aware of good work-arounds that are materially different from those already shown. Alternatively, feel free to post another answer with your work-around, and I will (at my earliest convenience :) ) add it here myself.

我鼓励其他人在这个答案中增加额外的工作,如果他们知道好的变通方法和已经显示出来的方法有很大的不同。或者,也可以在你的工作中发布另一个答案,我将(在我最方便的时候:)自己在这里添加它。

#2


2  

This is a bit of a workaround and doesn't explain the cause of the behavior you are describing, but it achieves what you are looking for:

这只是一个变通方法,并没有解释你所描述的行为的原因,但它实现了你想要的:

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>
    <Button HorizontalAlignment="Left" Content="Button" />
    <Grid Grid.Column="1"
          ShowGridLines="True">
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0"
                Margin="5"
                MinWidth="{Binding ElementName=Button2, Path=ActualWidth}"
                Content="B"
                x:Name="Button1" />
        <Button Grid.Column="1"
                Margin="5"
                Content="Button"
                x:Name="Button2" />
    </Grid>
</Grid>

为什么嵌套网格中的星星尺寸不工作?

I have to assume that an Auto column width is calculated based on the minimum width of the child controls. I wasn't able to confirm or disprove this looking through any documentation, but it is probably expected behavior. In order to force the Button controls to take up equal size, you can just bind the MinWidth property to the ActualWidth of the other Button. In this example, I only bound the Button1.MinWidth to the Button2.ActualWidth to illustrate your desired behavior.

我必须假设自动列宽度是基于子控件的最小宽度计算的。我无法通过任何文档来证实或反驳这一点,但这可能是预期的行为。为了使按钮控件具有相同的大小,只需将MinWidth属性绑定到另一个按钮的实际宽度。在这个例子中,我只绑定了Button1。MinWidth Button2。实际宽度来说明你想要的行为。

Also, please ignore the Button.Height, I didn't bother to set them different than the default.

另外,请忽略按钮。高度,我没有设置它们与默认值不同。

#3


1  

From the documentation (Modern apps) (WPF):

来自文档(现代应用)(WPF):

starSizing A convention by which you can size rows or columns to take the remaining available space in a Grid.

按约定对行或列进行大小调整,以获取网格中剩余的可用空间。

It doesn't cause more minimum space to be requested, it affects distribution of space in excess of the minimum.

它不会导致需要更多的最小空间,它会影响超过最小空间的分布。