寻找WPF Grid ColumnSpan行为的解释

时间:2011-04-15 15:33:56

标签: wpf xaml grid columnspan

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

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

  1. 为什么第0列的宽度不被尊重?
  2. 为什么删除滚动查看器时列大小调整的行为会有所不同?
  3. 我感谢任何解释!这对我来说真是一个难题。

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

4 个答案:

答案 0 :(得分:6)

这是一个非常好的问题,并测试了我们直觉的极限。它揭示了Grid的布局逻辑的实现细节。

100的宽度没有被尊重,因为:

  1. 第三列中没有任何内容可以使网格给它宽度。
  2. 第二行中的长文本比前两列中的长文本宽。
  3. 当Grid的宽度不受其父级约束或设置时,其布局逻辑显然会拉伸第一列而不是最后一列。
  4. 通过在第一列上放置MaxWidth,您将约束Grid的布局逻辑,因此它将移动到第二列并对其进行拉伸。你会注意到在这种情况下它将超过100。

    但是,只要Grid的宽度设置为特定值或受其父级约束(例如,当窗口中没有ScrollViewer时),Grid的宽度就有一个特定值,第三列的宽度设置为均匀虽然它是空的。现在Grid的自动调整大小代码已停用,它不再延伸任何列以尝试挤入该文本。你可以通过在Grid上放一个特定的宽度来看到这一点,即使它仍然在ScrollViewer中。

    编辑:现在我在原始帖子中读到了MSDN支持的答案,我认为这是正确的,这意味着这可能是附加属性的实现而不是网格的结果本身。但是,原则是一样的,希望我的解释足够清楚,以便了解这里的微妙之处。

答案 1 :(得分:3)

简答:
它因为以下组合:
1. ScrollViewer的存在,允许网格(如果愿意)采取任何所需的大小。
2.网格没有明确的宽度。
3.未指定宽度的列(第2列),将其设置为1 *,其最终大小取决于网格和其他列的大小。
4.具有三列colspan的TextBlock。

如果你:
1.删​​除滚动查看器,网格只允许生长到窗口的客户区域(在您的示例中大约为278),并且长文本块必须适合此宽度,否则将其修剪。
2.设置Grid的显式宽度,再次修剪文本块以适应。
3.设置第2列的显式宽度,它为网格提供固定宽度(100 + 100 + width_of_col2),再次修剪文本块以适应。
4.删除colspan,不包含它的列和定义的固定宽度将采用该宽度。

以下是发生的事情:
这是粗略的,而不是measure and arrange passes的确切解释,但是,它应该给出一个公平的想法。

首先col0为100,col1为100,col2为0.基于此网格的大小为100 + 100 + 0 = 200。当Grid要求测量其子(文本块)时,它会看到前两个文本块适合其列的宽度。但是,第三个文本块需要288.因为,网格没有定义任何宽度并且在滚动查看器中,如果其中一个孩子需要它,它可以增加其大小。 Grid现在已将其规模从200增加到288(即88)。这意味着该文本块跨越的所有列(所有三个)将扩展88 / 3~ = 29像素。这使得col0 = 100 + 29 = 129,col1 = 100 + 29 = 129,col2 = 0 + 29。

试试这个:
包含一个矩形,将其放在col2中并将矩形宽度设置为20。

这就是发生的事情:
首先考虑col0和col1,每个100都满意,因为它们各自的文本块需要少于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 :(得分: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时,只为ScrollViewer提供一个非常大的值,例如Canvas<Grid MaxWidth="1000000"> 。我建议像:

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

这种方法通过防止大小约束具有正无穷大的探测值来避免错误,同时实际上实现了相同的效果。

但是这个:

{{1}}

会触发错误。

答案 3 :(得分:0)