寻找 WPF Grid ColumnSpan 行为的解释

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5679081/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:48:03  来源:igfitidea点击:

Looking for explanation for WPF Grid ColumnSpan behavior

wpfxamlgridcolumnspan

提问by Dale Barnard

I asked a question at http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5c7f5cdf-4351-4969-990f-29ce9ec84b87/, but still lack a good explanation for a strange behavior.

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

Running the following XAML shows that the TextBlock in column 0 is width greater than 100 even though the column is set to width 100. I think that the strangeness may have something to do with it being wrapped in a ScrollViewer, but I don't know why. If I set a MaxWidth on the columns, it works fine, but setting Width does not.

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

  1. Why is the width of column 0 not being honored?
  2. Why does the column sizing behave differently when you remove the scroll viewer?
  1. 为什么不遵守第 0 列的宽度?
  2. 为什么删除滚动查看器时列大小的行为会有所不同?

I appreciate any explanation! This is a real puzzle to me.

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

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

回答by Jerry Bullard

This is a very good question and tests the limits of our intuition. It reveals the implementation details of the Grid's layout logic.

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

The width of 100 is not being honored because:

100 的宽度不被尊重,因为:

  1. There is nothing in the third column that causes the grid to give it width.
  2. The long text in the second row is wider than can fit in the first two columns.
  3. When the width of the Grid is not constrained or set by its parent its layout logic evidently stretches the first column instead of the last column.
  1. 第三列中没有任何内容导致网格为其提供宽度。
  2. 第二行中的长文本比前两列中的内容要宽。
  3. 当 Grid 的宽度不受其父项约束或设置时,其布局逻辑显然会拉伸第一列而不是最后一列。

By putting a MaxWidth on the first column, you are constraining the Grid's layout logic, so it moves on to the second column and stretches it. You'll note it will be wider than 100 in that scenario.

通过在第一列上放置 MaxWidth,您将限制 Grid 的布局逻辑,因此它会移动到第二列并对其进行拉伸。您会注意到在这种情况下它会比 100 宽。

However, as soon as the Grid's width is set to a specific value or is constrained by its parent (e.g. when no ScrollViewer in the Window), the Grid's width has a specific value, and the third column gets a width set even though it is empty. Now the Grid's auto-size code is deactivated, and it no longer stretches any of your columns to try to squeeze in that text. You can see this by putting a specific width on the Grid, even though it is still in the ScrollViewer.

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

Edit:Now that I read the answer of the MSDN support in your original thread, I believe it is correct, meaning this is probably the result of the implementation of the attached property and not the grid itself. However, the principle is the same, and hopefully my explanation is clear enough to make sense of the subtlety here.

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

回答by publicgk

Short Answer:
Its because of the combination of:
1. Presence of ScrollViewer which allows grid (if it wishes) to take any desired size.
2. The grid not having explicit width.
3. A column (Column 2) whose width has not been specified, which sets it to 1*, leaving its final size dependant on size of grid and other columns.
4. TextBlock which has colspan over three columns.

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

If you:
1. Remove the scrollviewer, the grid is allowed to grow only till the client area of window (which comes to be about 278 in your example), and the long textblock has to fit within this width otherwise its trimmed.
2. Set explicit width of Grid, which again trims textblock to fit.
3. Set explicit width of Column 2, which provides a fixed width to grid (100+100+width_of_col2), which again trims textblock to fit.
4. Remove the colspan, the columns which do not contain it and have fixed width defined, will take that width.

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

Here's what's happening:
This is crude and not exact explanation of the measure and arrange passes, however, it should give a fair idea.

To start with col0 is happy with 100, col1 with 100 and col2 with 0. Based on this grid's size would be 100+100+0=200. When Grid requests its children (textblocks) to be measured, it sees that first two textblocks fit within the width of their columns. However, the third textblock needs 288. Since, grid isn't having any width defined and its within a scrollviewer, it can increase its size if one of its child needs it. The Grid has now to increase its size from 200 to 288 (i.e. by 88). This means each column to which that textblock spans (all three of them) will expand by 88/3~=29 pixels. This makes col0=100+29=129, col1=100+29=129, col2=0+29.

Try this:
Include a rectangle, put it in col2 and set width of rectangle to 20.

This is what's happening:
To start with col0 and col1 are happy with 100 each as their individual textblocks need less than that. col2 is happy with 20 as rectangle in it needs that. Based on this grid's width would be 100+100+20=220. However, because of the columnspanning textblock the Grid has to increase its size from 220 to 288 (i.e. by 68). This means each column to which that textblock spans (all three of them) will expand by 68/3~=23 pixels. This makes col0=100+23=123, col1=100+23=123, col2=20+23=43.

这是发生的事情:
这是对措施和安排传球的粗略且不准确的解释,但是,它应该给出一个公平的想法。

开始时 col0 是 100,col1 是 100,col2 是 0。基于这个网格的大小将是 100+100+0=200。当 Grid 请求测量其子项(文本块)时,它会看到前两个文本块适合其列的宽度。然而,第三个文本块需要 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.

哈。

回答by Rick Sladkey

Here is another example that shows the problem using a Canvasinstead of a ScrollViewer:

这是使用 aCanvas而不是 a显示问题的另一个示例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>

This example shows that when given unlimited space, the first two columns are incorrectly expanded by 33%. I do not have working reference source to debug this right now because SP1 broke .NET4 reference source but frankly pinpointing this to the line in the source file is not going to help you so let's not go that route.

此示例显示,当给定无限空间时,前两列错误地扩展了 33%。我现在没有可用的参考源来调试它,因为 SP1 破坏了 .NET4 参考源,但坦率地说,将其定位到源文件中的行对您没有帮助,所以我们不要走这条路。

Instead, we'll agree that this is definitely a bug and we can prove that it's a bug by setting Grid.MaxWidthto progressively larger values and the widths of two columns remain both at 100 no matter how large it gets. But if you leave Grid.MaxWidthunset and place the Gridinside of a Canvasthen the value during measure will be double.PositiveInfinityand this value with produce column widths of 133. As a result we can speculate that some how the special case of a size constraint of positive infinity is not handled correctly during the column sizing calculations.

相反,我们会同意这绝对是一个错误,我们可以通过设置Grid.MaxWidth逐渐增大的值来证明它是一个错误,并且无论两列的宽度有多大,都保持在 100。但是,如果您不Grid.MaxWidth设置并放置Grid在 a的内部,Canvas则测量期间的值将是,double.PositiveInfinity并且该值产生的列宽为 133。因此,我们可以推测一些如何不处理正无穷大大小约束的特殊情况在列大小计算期间正确。

Luckily, that same experiment provides a simple workaround: simply supply an absurdly large value for Grid.MaxWidthwhen the Gridis used inside another control that allows it unlimited space, such as a ScrollVieweror a Canvas. I recommend something like:

幸运的是,同一个实验提供了一个简单的解决方法:在另一个控件中使用Grid.MaxWidth时,只需提供一个大得离谱的值,Grid以允许其无限空间,例如 aScrollViewer或 a Canvas。我推荐类似的东西:

<Grid MaxWidth="1000000">

This approach avoids the bug by preventing the size constraint from having the probematic value of positive infinity, while practically achieving the same effect.

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

But this:

但是这个:

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

will trigger the bug.

会触发bug。

回答by Dale Barnard