【问题标题】:Looking for explanation for WPF Grid ColumnSpan behavior寻找 WPF Grid ColumnSpan 行为的解释
【发布时间】:2011-04-15 15:33:56
【问题描述】:

我在 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. 为什么当您移除滚动查看器时,列大小的行为会有所不同?

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

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

【问题讨论】:

    标签: wpf xaml grid columnspan


    【解决方案1】:

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

    100 的宽度没有得到遵守,因为:

    1. 第三列中没有任何内容导致网格为其指定宽度。
    2. 第二行中的长文本比前两列更宽。
    3. 当 Grid 的宽度不受其父级约束或设置时,其布局逻辑显然会拉伸第一列而不是最后一列。

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

    但是,一旦 Grid 的宽度设置为特定值或受其父级约束(例如,当 Window 中没有 ScrollViewer 时),Grid 的宽度具有特定值,并且第三列甚至设置了宽度虽然它是空的。现在 Grid 的自动大小代码已停用,它不再拉伸任何列以尝试挤入该文本。您可以通过在 Grid 上放置特定宽度来查看这一点,即使它仍在 ScrollViewer 中。

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

    【讨论】:

    • 感谢您的周到回答。正如我在对该线程中先前答案的评论中所说,这让我想知道:1.为什么 MaxWidth 比固定宽度更受尊重? 2.您认为这是Grid控件的缺陷还是MSDN上的Grid控件文档的缺陷?我在 MSDN 中没有看到任何接近描述这种行为的内容。我很想将此记录为缺陷。
    • 这不是“尊重” MaxWidth 而不是 Width 的问题。一个是约束,另一个是初始起始值。如果存在错误,则 ColSpan 附加属性会以幼稚的方式影响布局。在出现此 ColSpan 项之前,第三列根本没有宽度,即使它的宽度为“*”,当没有足够的宽度容纳 ColSpan 项时,Grid 也会开始从左到右加宽列。直觉上,这感觉不对,但我不会称其为错误。这肯定是一个有漏洞的抽象。
    • MS 可以更改布局行为以查找宽度为“*”的列首先加宽(在从左到右加宽列之前)。但是,根据当前的行为,可能会有很多代码。微软很可能会将其定义为标准并继续前进。但由于这太违反直觉且看似错误,它们可能会让我们大吃一惊。
    • 我同意,由于人们可能依赖于当前的行为,他们不会改变它。我只是建议他们记录不直观的行为。另外,我在 MSDN 文档中没有看到 MaxWidth 是一个约束,但 Width 是一个起始值。您是否在某处看到记录,或者您只是从证据中得出?感谢 cmets!
    【解决方案2】:

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

    如果您:
    1.删​​除滚动查看器,网格只能增长到窗口的客户区域(在您的示例中约为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。当 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。

    【讨论】:

    • 感谢您的周到回答。这让我想知道: 1. 为什么 MaxWidth 比固定宽度更受尊重? 2.您认为这是Grid控件的缺陷还是MSDN上的Grid控件文档的缺陷?我在 MSDN 中没有看到任何接近描述这种行为的内容。我很想将此记录为缺陷。 (我也在下一个答案中重复了这个评论,因为它同样适用于那里。)
    • 没有。我不相信这是一个缺陷。我相信宽度和高度是一种要求的值,考虑到测量和排列通道是递归的,可能不会得到尊重。这就是为什么提供 MaxWidth 和 MaxHeight(甚至 MinWidth 和 MinHeight)来帮助用户设置约束的原因。
    • From MSDN 回答您的问题 1:FrameworkElement 类公开了描述元素宽度特征的四个属性。这四个属性可能会发生冲突,当它们发生冲突时,优先值的确定如下:MinWidth 值优先于 MaxWidth 值,而 MaxWidth 值又优先于 Width 值。第四个属性 ActualWidth 是只读的,它报告由与布局过程的交互确定的实际宽度。
    • 在 MSDN 中似乎没有说 Width 是“一种请求值”而不是绝对值,因此表明存在文档缺陷。我也不清楚您对优先级的其他评论的意义是什么。如果没有指定 MaxWidth 和 MinWidth,什么优先于 Width?感谢您的回复。
    • 啊,我猜原因是我们正在讨论 ColumnDefinition 的宽度,它不是 FrameworkElement 而是 FrameworkContentElement。为混淆道歉。答案在于如何(以及为什么)实施网格。是时候提供赏金来吸引专家回答了。 :)
    【解决方案3】:

    这是另一个使用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 用于另一个允许无限空间的控件(例如ScrollViewerCanvas)中时,只需为Grid.MaxWidth 提供一个大得离谱的值.我推荐类似的东西:

    <Grid MaxWidth="1000000">
    

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

    但是这个:

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

    会触发错误。

    【讨论】:

    • 有趣的是,您发现将 Grid.MaxWidth 设置为 NaN 以外的值。我想我最好将此作为缺陷报告。
    【解决方案4】:

    【讨论】:

      猜你喜欢
      • 2019-01-26
      • 2018-03-23
      • 1970-01-01
      • 2014-06-11
      • 2020-11-29
      • 2012-10-24
      • 1970-01-01
      • 1970-01-01
      • 2021-04-06
      相关资源
      最近更新 更多