为什么嵌套网格中的星形大小不起作用?

考虑以下XAML:

     

在上面,除了一个之外的所有ColumnDefinition值都使用Width的默认值,即“ "*" ,即“star sizing”。 一个例外是包含嵌套Grid控件的列,该控件设置为"Auto"

我希望这种方式的工作方式如下:

  • 外部Grid根据其内容的需要调整第二列的大小,然后将控件的剩余宽度分配给第一列。
  • 内部Grid将其可用空间均匀分配到两列。 毕竟,它们都设置为使用星形大小调整,并且星形大小应该将GridLength属性(在这种情况下为宽度)设置为可用空间的加权分布。 此内部Grid的最小布局大小(外部Grid需要计算其第二列的宽度)是均匀分布宽度的星形大小的列的总和(即,在这种情况下,是列的宽度的两倍)最广泛的内容)。

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

错误分布的列宽

如果我没有外部网格,它只是按预期工作,即只需使内部网格成为窗口中唯一的网格:

正确分布的列宽

两列被强制为相同的大小,然后当然左手按钮被拉伸以适应其包含单元格的大小(这就是我想要的……最终目标是这两个按钮具有相同的宽度,网格列提供了完成该布局的布局。

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

在此处输入图像描述

但我真的想更全​​面地了解如何实现这一点,以便在更复杂的场景中,我可以在嵌套的Grid控件中使用star-sizing。

似乎以某种方式,包含在另一个Grid控件的单元Grid正在改变为内部Grid控件计算星形大小的方式(或防止星形大小完全没有任何影响)。 但为什么会这样呢? 我是否遗漏了(再次)WPF的一些深奥的布局规则,将其解释为“按设计”行为? 或者这仅仅是框架中的错误?

更新:

我理解Ben的答案意味着考虑每列的最小尺寸 ,星形尺寸应仅分布剩余空间。 但这不是人们在其他场景中看到的。

例如,如果包含内部网格的列已明确resize,则对内部网格的列使用星号大小会导致列的大小均匀,就像我期望的那样。

即XAML:

    <!--  -->   

产生这个输出:

在此处输入图像描述

换句话说,在最坏的情况下,我希望WPF首先计算内部网格的最小尺寸,而不考虑星形尺寸(例如,如果短按钮需要10个像素而长按钮需要70个,那么总宽度将是80),然后仍然均匀分布列宽(即在10/70示例中,每列将最多40像素,截断较长的按钮,类似于上面的图像)。

为什么星形尺寸有时会在列间均匀分布宽度,有时候不是?

更新#2:

下面是一个简单的例子,它清楚而明确地显示了WPF如何根据是否是计算Grid宽度的方式来处理星形大小,或者您是:

                              

运行程序时,您会看到:

在此处输入图像描述

WPF忽略了星号,将每个列宽设置为最小值。 如果你只是单击窗口边框,就好像要调整窗口大小(你甚至不必在任何地方实际拖动边框), Grid布局重做,你得到这个:

在此处输入图像描述

此时,应用星形大小(正如我所期望的那样),并根据XAML声明对列进行比例调整。

我同意Ben的回答强烈暗示这里可能发生的事情:

正如Ben指出的那样,为了计算内部Grid对象的最小宽度,WPF忽略了星大小(也许是合理的……我认为存在进行诚实辩论的空间,但显然这是一种可能且合法的设计)。

什么不清楚(以及Ben没有回答)是为什么这应该意味着当计算内部Grid的列宽时, 也会忽略星形大小。 由于在外部施加宽度时,比例宽度将导致内容被截断,如果需要保留这些比例,为什么在根据内容的最小所需大小自动计算宽度时不会发生同样的事情。

即我仍在寻找我的问题答案。

与此同时,恕我直言有用的答案包括解决问题的方法。 虽然不是我问题本身的实际答案,但它们显然对任何可能遇到问题的人都有帮助。 所以我写这个答案来巩固所有已知的解决方案(无论好坏,WPF的一个重要特征是,似乎总有至少几种不同的方法来实现相同的结果:))。

解决方法#1:

对内部网格对象使用UniformGrid而不是Grid 。 此对象与Grid不具有所有相同的function,当然不允许任何列具有不同的宽度。 因此它可能在所有情况下都没用。 但它很容易解决这里的简单问题:

            

解决方法#2:

将较小内容对象的MinWidth属性(例如,此处,网格中的第一个Button )绑定到较大内容对象的ActualWidth属性。 这当然需要知道哪个对象具有最大宽度。 在本地化方案中,这可能会有问题,因为除了文本资源之外,XAML还必须进行本地化。 但无论如何这有时是必要的,所以…… 🙂

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

                

一个变体(为简洁起见,我将不在此处)将使用MultiBinding ,它将所有相关控件作为输入并返回集合的最大MinWidth 。 当然,然后这个绑定将用于每个ColumnDefinitionWidth ,以便所有列都显式设置为最大的MinWidth

绑定方案还有其他变体,具体取决于您要使用和/或设置的宽度。 这些都不是理想的,不仅仅是因为潜在的本地化问题,还因为它在XAML中嵌入了更明确的关系。 但在许多情况下,它将完美地运作。

解决方法#3:

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

例如:

                

这种方法允许人们使用Grid ,因此获得该对象的所有正常特征,同时解决关注的特定行为。 我希望它不会干扰SharedSizeGroup任何其他合法使用。

但是,它确实意味着"Auto"大小调整,并排除了"*" (星级)大小调整。 在这个特殊情况下,这不是问题,但是在UniformGrid解决方案的情况下,它在尝试将其与其他大小调整结合时确实限制了一个选项。 例如,让第三列使用"Auto"并希望SharedSizeGroup列获取Grid的剩余空间。

尽管如此,这在许多场景中都可以正常运行而不会有任何问题。

解决方法#4:

我最后重新审视了这个问题,因为我遇到了主题的变化。 在这种情况下,我正在处理一种情况,我希望对要resize的列具有不同的比例。 所有上述变通方法都假定大小相等的列。 但我希望(例如)一列有25%的空间,另一列有75%的空间。 和以前一样,我希望Grid的总大小适应所有列所需的最小宽度。

这个解决方法涉及简单地自己做我觉得WPF应该做的计算。 即,获取每列内容的最小宽度以及指定的比例大小,计算Grid控件的实际宽度。

这是一个可以做到这一点的方法:

 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; } 

此代码找到的最小宽度仍然可以容纳列的最小宽度和比例大小的每个星形大小的列,以及不使用星形大小的其余列。

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

对于行高,类似的方法也会做同样的事情。

解决方法#5:

我想有人指出:可以简单地明确指定Grid大小。 这仅适用于预先知道大小的内容,但在许多情况下,这也很好。 (在其他情况下,它会截断内容,因为即使指定的大小太小,当它明确时 ,WPF继续并应用星号大小比例)。

如果他们意识到与已经显示的那些有很大不同的好的解决方法,我鼓励其他人为这个答案增加额外的解决方法。 或者,随意在你的解决方案中发布另一个答案,我会(在我最早的方便:) :)自己添加它。

这是一个解决方法,并没有解释您描述的行为的原因,但它实现了您正在寻找的:

                

屏幕截图

我必须假设Auto列宽是根据子控件的最小宽度计算的。 我无法通过任何文档确认或反驳这一点,但这可能是预期的行为。 为了强制Button控件占用相同的大小,您只需将MinWidth属性绑定到另一个ButtonActualWidth 。 在这个例子中,我只将Button1.MinWidth绑定到Button2.ActualWidth来说明你想要的行为。

另外,请忽略Button.Height ,我没有费心将它们设置为与默认值不同。

从文档(现代应用程序) (WPF) :

starSizing一种约定,通过该约定,您可以调整行或列的大小以获取网格中剩余的可用空间。

它不会导致请求更多的最小空间,它会影响超出最小值的空间分布。