代码之家  ›  专栏  ›  技术社区  ›  Phil Wright

有人知道用生成单元格调整网格列大小的算法吗?(WPF)

  •  2
  • Phil Wright  · 技术社区  · 15 年前

    当子元素总是包含在单个网格单元中(即没有列/行跨越)时,使用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,没有找到任何描述跨越元素如何影响列宽的内容。

    3 回复  |  直到 14 年前
        1
  •  5
  •   Martin Harris    15 年前

    老实说,我读这篇文章的时候不相信你,所以我不得不亲自去测试它,事实上你是对的。不过,我不认为你会有任何运气找到规则,因为进一步的调查让我相信这是WPF网格中的一个bug。我做了一些测试,看看我是否能找出行为,但是如果你只是想知道最后的结论,请随意滚动到下面的文章。

    对不起,如果结果不清楚的话,我是怎么写出来的。前三个数字是我为每列输入的大小调整值,其余的是结果大小。

    试验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像素,第1行的第1列的第二个控件宽80像素

    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. 如果自动列在跨度中使用,并且无法从其他地方获得其大小,则测量系统将中断,并可能导致图形损坏。

    所以,我想我们可以把这些规则结合成一个整体的理念:

    如果一个元素跨越多个列,并且其中至少有一个(但不是全部)列是自动调整大小的,则必须有另一个不跨越的元素来为自动调整大小的列提供大小。否则,这种行为充其量是出人意料的,充其量是腐败。

    尽管可以向微软提出一个bug,但由于这是网格控件的定义行为,我想我们在WPF的整个生命周期中都会一直使用它。

        2
  •  0
  •   Gabe Timothy Khouri    14 年前

    以下是操作方法:

    • 如果请求的大小(childelement.desiredSize)符合范围(范围)的首选大小。请求的大小根据以下逻辑分布:

      • 自动定义不会参与分发;如果设置,它们将只获得minsize。
      • 对于所有非自动定义,请求的大小在它们之间平均分布;不超过每个的首选大小。
    • 如果请求的大小大于首选大小,但适合范围的最大大小。 请求的大小根据以下逻辑分布:

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

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

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

      • 如果相等大小小于最大大小。

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

          • 如果相等大小大于或等于最大大小。
        • 所有定义的最小尺寸均为等号。

        3
  •  -1
  •   Arcturus    15 年前

    我不知道互联网上有没有完整的描述。

    我知道,自动意味着它会问孩子控制他们想要多大。星号(*)表示它将占用它剩下的所有房间…

    编辑:即时更新.. 宽度(ColumnDefinition)和高度(RowDefinition)是网格长度结构:

    GridLength on MSDN