【问题标题】:Anyone know the algorithm used to size Grid columns with spanning cells? (WPF)任何人都知道用于调整跨单元格的网格列大小的算法吗? (WPF)
【发布时间】:2009-04-07 05:01:48
【问题描述】:

当您的子元素始终包含在单个网格单元中(即没有列/行跨越)时,使用 WPF 网格控件很容易。固定宽度的列保持请求的固定宽度,自动列确实会将自身调整为与列中最宽的单元格一样宽。星形列根据它们的相对星值共享任何剩余空间。世界上一切都很简单。

但是一旦你有一个跨越两列或更多列的单元格,列的宽度计算起来就会变得更加复杂,而且确实看起来违反直觉。这是我的意思的一个简单的例子。我们可以定义两列,第一列为自动,第二列固定为 30 像素。

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto"/>
    <ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>

现在我们定义一个跨越两列的按钮,其宽度恰好为 80 像素。 (在我的机器上使用我的字体和主题设置等...)

<Button Grid.ColumnSpan="2" Content="QWERTYUIOP"/>

我们需要在两个跨列中分配按钮的 80 像素宽度。鉴于第二列固定为 30,我希望为该列分配 30 个,其余 50 个分配给第一列。因为第一列是自动的,所以这似乎是显而易见的事情。

但是没有。如果您在实践中尝试这样做,您会发现第一列是 0 像素,而第二列已经变成了完整的 80。即使第二列被定义为固定的,因此不应该变得更大,它被拉伸到全宽的按钮。也许我在这里遗漏了一些东西,但这对我来说似乎不太合乎逻辑。

所以对于我的实际问题。是否有关于网格控件用于执行此计算的逻辑的完整描述,以便我可以完全理解它是如何工作的?我搜索了 MSDN 和 Google,但没有找到任何描述跨越元素如何影响列宽的信息。

【问题讨论】:

    标签: wpf grid


    【解决方案1】:

    老实说,当我读到这篇文章时我不相信你,所以我不得不自己去测试它,你确实是对的。不过,我认为您不会有任何运气找到规则,因为进一步的调查使我相信这是 WPF 网格中的错误。我进行了一些测试,看看我是否能弄清楚这种行为,但如果你只是想知道最终结论,请随意翻阅以下谩骂。

    抱歉,如果结果不清楚,它们是我将它们写成作品的方式。前三个数字是我为每一列输入的尺寸值,其余的是结果尺寸。

    测试 1:

    <Grid Height="300" Width="300">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="..."/>
            <ColumnDefinition Width="..."/>
            <ColumnDefinition Width="..."/>
        </Grid.ColumnDefinitions>
        <Button Grid.ColumnSpan="2" Content="QWERTYUIOP"/>
    </Grid>
    

    网格 300 X 300 托管 80 像素宽的控件。网格有三列,控件跨越第 0 列和第 1 列:

    30  , auto, *    - column 0 - 80,  column 1 - 0,  column 2 - 220
    auto, auto, *    - column 0 - 40,  column 1 - 40, column 2 - 220
    auto, 30  , *    - column 0 - 0,   column 1 - 80, column 2 - 220
    *   , 30  , *    - column 0 - 135, column 1 - 30, column 2 - 135
    *   , 30  , 26*  - column 0 - 10,  column 1 - 30, column 2 - 260
    *   , auto, *    - column 0 - 150, column 1 - 0,  column 2 - 150
    *   , auto, 30*  - column 0 - 10,  column 1 - 0,  column 2 - 290
    30  , 30  , *    - column 0 - 30,  column 1 - 30, column 2 - 240
    *   , *   , 28*  - column 0 - 10,  column 1 - 10, column 2 - 280
    

    因此,我可以想出一些规则:

    1. 如果元素跨越自动调整大小的单元格和非自动调整大小的单元格,则自动调整大小的单元格将调整为 0
    2. 如果元素跨越固定单元格和自动单元格,则固定单元格的宽度将增加到可以包含整个对象的最小值
    3. 如果元素跨越 * 大小的单元格和自动大小的单元格,则 * 大小的单元格将具有其预期大小
    4. 如果一个元素跨越两个自动调整大小的单元格,它们将展开以适合对象并且宽度相等
    5. 如果元素跨越不包含自动单元格的单元格,则单元格将具有其预期大小

    测试 2:

    <Grid ShowGridLines="True" Height="300" Width="300">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="..."/>
            <ColumnDefinition Width="..."/>
            <ColumnDefinition Width="..."/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button Grid.ColumnSpan="2" Content="QWERTYUIOP"/>
        <Button Grid.Row="1" Grid.Column="1" Content="QWERTYUIOP"/>
    </Grid>
    

    网格 300 X 300,包含 3 列,其中一个控件位于第 0 行的第 0 列和第 2 列,宽度为 80 像素,第二个控件的宽度为 80 像素,仅位于第 1 行的第 1 列中

    30  , auto, *    - column 0 - 30,  column 1 - 80, column 2 - 190
    auto, auto, *    - column 0 - 0,   column 1 - 80, column 2 - 220
    auto, 30  , *    - column 0 - 0,   column 1 - 80, column 2 - 220 (*)
    *   , 30  , *    - column 0 - 135, column 1 - 30, column 2 - 135
    *   , 30  , 26*  - column 0 - 10,  column 1 - 30, column 2 - 260
    *   , auto, *    - column 0 - 110, column 1 - 80, column 2 - 110
    *   , auto, 30*  - column 0 - 7,   column 1 - 80, column 2 - 213
    30  , 30  , *    - column 0 - 30,  column 1 - 30, column 2 - 240
    *   , *   , 28*  - column 0 - 10,  column 1 - 10, column 2 - 280
    

    (*) 这并不是真正发生的事情。第 1 列的大小设置为 80,但仅绘制非跨越元素的一部分。我使用按钮作为我的元素,非跨区按钮的镶边填充了 80 像素宽的第一列,但文本被修剪为 30 像素的大小。基本上是完蛋了。

    通过这个测试,我可以再添加两条规则:

    1. 如果在跨度中使用自动列并且可以从某个地方获取其大小,它将按预期运行
    2. 如果在跨度中使用自动列并且无法从其他位置获取其大小,则测量系统将中断并可能导致图形损坏。

    所以,我想我们可以将这些规则组合成一种弧线设计理念:

    如果一个元素跨越多个列,并且这些列中的至少一个(但不是所有)列是自动调整大小的,则必须有另一个不跨越才能为自动调整大小的列提供大小。否则,该行为充其量是出乎意料的,最坏的情况是破坏性的。

    请随意向 Microsoft 提出一个错误,但由于现在这是网格控件的定义行为,我想我们在 WPF 的整个生命周期中都会坚持使用它。

    【讨论】:

    • 虽然这不是我所期望的算法,但它确实证实了我自己的结果,它被搞砸了!基本上我建议尽可能不要使用跨越单元格。
    【解决方案2】:

    这里是怎么做的:

    • 如果 requestedSize (childelement.DesiredSize) 适合范围 (span) 的首选大小。 RequestedSize 的分配逻辑如下:

      • 自动定义不参与分发;如果设置,它们只会变小。
      • 对于所有非自动定义,请求的大小在它们之间平均分配;不超过每个人的首选尺寸。
    • 如果 requestedSize 大于首选大小,但适合范围的最大大小。 RequestedSize 的分配逻辑如下:

      • 对于所有非自动定义,请求的大小在它们之间平均分配;不超过每个的最大尺寸。

      • 然后,上述步骤的剩余大小在自动定义之间平均分配。

    • 如果 requestedSize 大于范围的最大大小。 RequestedSize 按以下方式在所有定义之间平均分配:

      • 如果 equi-size 小于 maxSizes 的最大值。

        • 在这种情况下,大小的分布方式使得较小的定义比较大的定义增长得更快。

          • 如果 equi-size 大于或等于最大尺寸的最大值。
        • 所有定义都会收到 equalSize 作为它们的最小尺寸。

    【讨论】:

      【解决方案3】:

      我不知道互联网上某处是否有完整的描述..

      我知道,Auto 意味着它会询问孩子控制他们希望有多大。星号 (*) 表示它将占用它剩下的所有空间...

      编辑:即时更新.. 宽度(ColumnDefinition)和高度(RowDefinition)是GridLength结构体:

      GridLength on MSDN

      【讨论】:

      • 您刚刚给出的答案表明您没有答案。我的问题指出我已经知道什么是 Auto,*,Fixed 类型以及它们是如何工作的。我需要知道它们如何处理单元跨越。
      猜你喜欢
      • 2020-09-30
      • 2012-06-24
      • 2012-07-03
      • 1970-01-01
      • 2017-02-26
      • 2012-12-22
      • 2010-09-16
      • 1970-01-01
      • 2015-07-16
      相关资源
      最近更新 更多