2011-04-15 64 views
8

我在http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5c7f5cdf-4351-4969-990f-29ce9ec84b87/问了一个问题,但对于一个奇怪的行为仍然缺乏很好的解释。寻找WPF Grid GridSpan行为的解释

运行以下XAML显示列0中的TextBlock宽度大于100,即使列设置为宽度100。我认为这种奇怪可能与它被包装在ScrollViewer中有关,但我不知道为什么。如果我在列上设置MaxWidth,它工作正常,但设置宽度不。

  1. 为什么列0的宽度没有被遵守?
  2. 当您移除滚动查看器时,为什么列大小的行为会有所不同?

我很感激任何解释!这对我来说是一个真正的难题。

<Window x:Class="WpfApplication2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Width="300"> 
    <ScrollViewer HorizontalScrollBarVisibility="Auto" > 
     <Grid> 
      <Grid.RowDefinitions> 
       <RowDefinition Height="Auto" /> 
       <RowDefinition Height="Auto" /> 
      </Grid.RowDefinitions> 
      <Grid.ColumnDefinitions> 
       <ColumnDefinition Width="100" /> 
       <ColumnDefinition Width="100" /> 
       <ColumnDefinition /> 
      </Grid.ColumnDefinitions> 
      <TextBlock x:Name="textBlock" Text="{Binding ElementName=textBlock, Path=ActualWidth}" /> 
      <TextBlock Text="column 1" Grid.Column="1" /> 
      <TextBlock Grid.Row="1" Grid.ColumnSpan="3" Text="text here that is wider than the first two columns combined" /> 
     </Grid> 
    </ScrollViewer> 
</Window> 

回答

6

这是一个很好的问题,并测试我们的直觉的限制。它揭示了网格布局逻辑的实现细节。

的100宽度没有被兑现,因为:

  1. 没有什么在导致电网给它宽度的第三列。
  2. 第二行的长文字比前两列的宽。
  3. 当网格的宽度不受它的父节点的约束或设置时,它的布局逻辑显然拉伸了第一列而不是最后一列。

通过把一个MaxWidth在第一列上,你都制约了电网的布局逻辑,所以它移动到第二列和延伸它。你会注意到在这种情况下它会比100宽。

然而,只要电网的宽度被设置为一个特定值或由它的父约束(例如,当在窗口中没有的ScrollViewer),网格的宽度具有特定值,和第三列得到的宽度设定即使它是空的。现在,网格的自动尺寸代码已停用,并且不再延伸任何列以试图挤入该文本。您可以通过在网格上放置特定宽度来查看,即使它仍在ScrollViewer中。

编辑:现在,我看了你的原始线程的MSDN支持的答案,我相信这是正确的,这意味着这可能是附加属性的实现,而不是网格本身的结果。然而,原理是一样的,希望我的解释足够清楚地理解这里的微妙之处。

+0

谢谢你的深思熟虑的答案。正如我在此主题中对早期答案的评论中所说的那样,这让我怀疑:1.为什么MaxWidth的宽度超过固定宽度? 2.您认为这是网格控件还是MSDN网格控件文档中的缺陷?我没有看到MSDN中接近描述此行为的任何内容。我很想将此作为缺陷记录下来。 – 2011-04-18 18:19:45

+1

这不是“宽限”宽度超过MaxWidth的问题。一个是约束条件,另一个是最初的初始值。如果存在错误,那么ColSpan附加属性会影响布局,这是天真的方式。在ColSpan项目出现之前,第三列完全没有宽度,即使宽度为“*”,当没有足够的宽度以适合ColSpan项目时,网格开始将列从左向右扩展。直觉上,这感觉不对,但我不会说它是一个错误。这确实是一个漏洞抽象。 – 2011-04-29 22:26:57

+1

MS可以更改布局行为以查找宽度为“*”的列以首先展开(在将列从左向右扩展之前)。但是,根据当前行为,可能会有很多代码。微软可能会将其定义为标准并继续前进。但是因为这很违反直觉,看起来错误,所以他们可能会让我们吃惊。 – 2011-04-29 22:34:22

3

短答案:
因为组合及其:
1的ScrollViewer的存在,其允许电网(如果希望这样)采取任何期望的尺寸。
2.网格没有明确的宽度。
3.未指定宽度的列(第2列)将其设置为1 *,其最终大小取决于网格和其他列的大小。
4. TextBlock有三栏的colspan。

如果您:
1.拆下ScrollViewer中,网格被允许只增长至窗口的客户区域(其中涉及到在你的例如约278),长文本块必须适应这个范围内否则修剪宽度。
2.设置网格的显式宽度,它再次修剪文本块以适合。
3.设置第2列的显式宽度,该宽度向网格(100 + 100 + width_of_col2)提供固定宽度,该宽度再次修剪文本块以适合。
4.删除colspan,不包含它的列和固定宽度的列将采用该宽度。

这里发生的事情:
这是原油,而不是measure and arrange passes的确切的解释,但是,它应该给一个公平的想法。

以col0开头的是100,col1和100,col2和0。基于这个网格的大小将是100 + 100 + 0 = 200。当网格要求测量其子(文本块)时,它会发现前两个文本块符合其列宽。然而,第三个文本块需要288.因为,网格没有定义任何宽度,并且它在滚动查看器内,所以如果它的一个孩子需要它,它可以增加它的大小。电网现在已经从200个增加到288个(即88个)。这意味着该文本块跨越的每一列(全部三个)将扩展88/3〜= 29个像素。这使得col0 = 100 + 29 = 129,col1 = 100 + 29 = 129,col2 = 0 + 29。

试试这个:
包括一个矩形,把它放在COL2并设置矩形的宽度为20

这是发生了什么:
要与COL0开始和COL1很高兴能与各100他们的个别文本块需要的比这少。 col2很满意20,因为矩形需要它。基于这个网格的宽度将是100 + 100 + 20 = 220。但是,由于列扩展的文本块,网格必须将其大小从220增加到288(即增加68)。这意味着该文本块跨越的每一列(全部三个)将扩展68/3〜= 23个像素。这使得col0 = 100 + 23 = 123,col1 = 100 + 23 = 123,col2 = 20 + 23 = 43。

HTH。

2

下面是另一个例子示出了使用的Canvas代替ScrollViewer问题:

<Canvas> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="100"/> 
      <ColumnDefinition Width="100"/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TextBlock Grid.Column="0" x:Name="textBlock1" Text="{Binding ElementName=textBlock1, Path=ActualWidth}"/> 
     <TextBlock Grid.Column="1" x:Name="textBlock2" Text="{Binding ElementName=textBlock2, Path=ActualWidth}"/> 
     <TextBlock Grid.Row="1" Grid.ColumnSpan="3" Width="300"/> 
    </Grid> 
</Canvas> 

这个例子表明,给定的无限空间的情况下,前两列错误地通过33%的膨胀。我现在没有工作参考源来调试这个,因为SP1打破了.NET4的参考源,但坦率地说,把它指向源文件中的行不会帮助你,所以我们不要去那条路。

相反,我们会同意这肯定是一个错误,我们可以通过将Grid.MaxWidth设置为逐渐增大的值来证明这是一个错误,并且无论其大小如何,两列的宽度都保持在100。但是,如果您将Grid.MaxWidth取消设置并将Grid置于Canvas的内部,则度量期间的值将为double.PositiveInfinity,并且此值生成列宽度为133.因此,我们可以推测一些大小约束为正的特例在列大小计算期间无法正确处理。

幸运的是,同样的实验提供了一种简单的解决方法:简单地提供用于Grid.MaxWidth荒谬大的值,当被Grid另一个控制允许其无限空间,如一个ScrollViewerCanvas内部使用。我建议是这样的:

<Grid MaxWidth="1000000"> 

这种方法避免了通过防止大小限制从具有正无穷大的probematic值的bug,而几乎达到相同的效果。

但这:

<Grid MaxWidth="{x:Static sys:Double.PositiveInfinity}"> 

将触发错误。