【问题标题】:How to resize WPF DataGrid to fit its content?如何调整 WPF DataGrid 的大小以适应其内容?
【发布时间】:2011-01-02 10:07:20
【问题描述】:

目标

我想为 DataGrid(标准,来自 WPF)设置这样的大小,以便所有单元格(文本)都完全可见。我有一个带有 DockPanel 的窗口,里面有 DataGrid,所以当我调整窗口大小时,所有嵌套的小部件(DockPanel 和 DataGrid)都会相应地调整大小。

示例(edit-1)

假设您有一个 100 像素宽的窗口,并且您有一个包含一列的 DataGrid,其中的单元格是“快速棕色狐狸...”(400 像素宽)。因此,DataGrid 应调整为 400 像素(可能更多,因为填充),Window 也应该调整为 400 像素(也更多,因为填充)。

我没有找到任何标准方法来做到这一点(AFAIK WPF 提供了将内容剪辑到所需宽度的方法,我的问题正好相反),所以我想出了这种丑陋的解决方法,但效果不太好。

解决方法

  1. 遍历 DataGrid 标题(假设它们只是字符串)并计算文本所需的宽度
  2. 遍历每列的 DataGrid 行(假设它们是 TextBlock 或 TextBox)并计算文本所需的最大宽度 -- 为 TextBlock/TextBox 添加水平填充和为 DataGrid 单元格添加水平边距
  3. 汇总列的 DataGrid ActualWidth 与 (2) 中计算的最大宽度之间的所有差异
  4. 按照 (3) 中计算的差值增加窗口宽度

问题

我做了几个测试,在某些情况下计算的宽度太大(这是小问题),在某些情况下太小了。问题从它的核心过程开始——计算 TextBox/TextBlock 所需的宽度,计算的宽度总是比它应该的小 1 个单位(如果我将宽度设置为计算的 1,文本中的 1 个像素总是被剪裁)。

那么我在这里忽略了哪个因素?或者更好 -- 是否已经有一些方法可以调整 DataGrid 的大小以适应其内容?

代码

计算文本所需的宽度(此处为 TextBlock):

    public static double TextWidth(this TextBlock widget, string text)
    {
        var formattedText = new FormattedText(text, // can use arbitrary text
                                              System.Globalization.CultureInfo.CurrentCulture,
                                              widget.FlowDirection,
                                              widget.FontFamily.GetTypefaces().FirstOrDefault(),
                                              widget.FontSize, 
                                              widget.Foreground);

        return formattedText.Width+widget.Padding.Left+widget.Padding.Right;
    }

调整窗口大小以适应 DataGrid 内容(ugly_factor 是丑陋的解决方法;-)因为我不知道如何正确修复它,所以我将其设置为 1.3,这样我的窗口“永远不会”太小):

    public static void AdjustWidthToDataGrid(this Window window, DataGrid dataGrid, double ugly_factor)
    {
        var max_widths = dataGrid.Columns.Select(it => window.TextWidth(it.Header as string) 
                                                                        * ugly_factor).ToArray();

        foreach (var row in Enumerable.Range(0, dataGrid.Items.Count))
            foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
            {
                var cell = dataGrid.GetCell(row, col);
                double width = 0;
                if (cell.Content is TextBlock)
                    width = (cell.Content as TextBlock).TextWidth();
                else if (cell.Content is TextBox)
                    width = (cell.Content as TextBox).TextWidth();

                if (cell.Content is FrameworkElement)
                {
                    var widget = cell.Content as FrameworkElement;
                    width = width + widget.Margin.Left + widget.Margin.Right;
                }

                max_widths[col] = Math.Max(max_widths[col], 
                                           width*ugly_factor+cell.Padding.Left+cell.Padding.Right);
            }

        double width_diff = 0;

        foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))
            width_diff += Math.Max(0,max_widths[col] - dataGrid.Columns[col].ActualWidth);

        if (width_diff > 0)
            window.Width = window.ActualWidth+ width_diff;
    }

【问题讨论】:

  • 我对你的调整大小逻辑有点不知所措。首先,如果您设置窗口的 SizeToContent="WidthAndHeight",那么窗口将显示为数据网格的完整大小。另一方面,如果您以某个预定义的大小显示一个窗口,并且您想在显示窗口后调整窗口大小,那么此时将大小调整为所需的数据网格是非常反直觉的。因为调整大小会从 100px 跳到 400px,但是如果我只想调整到 250px 怎么办...(假设您通过拖动窗口的角来调整大小)
  • 哦,伙计,我花了整整一周的时间来编写和检查我的代码,它与 SizeToContent 相同,但方式很糟糕,现在我知道有一个选项吗? ;-) 生活具有讽刺意味... Marko,你救了我。请将您的评论重新发布为答案,我将其标记为答案。

标签: c# wpf layout datagrid


【解决方案1】:

如果我理解你的问题并且你想:

  1. 一种 DataGrid,其中列的宽度与其最宽的内容一样宽。
  2. DataGrid 适合其内容。

这可以通过数据绑定来实现:

<DataGrid AutoGenerateColumns="False"
          EnableRowVirtualization="True"
          Height="111"
          HorizontalAlignment="Left"
          ItemsSource="{Binding}"
          Margin="72,203,0,0"
          Name="dataGrid"
          RowDetailsVisibilityMode="VisibleWhenSelected"
          VerticalAlignment="Top"
          Width="{Binding Path=ActualWidth, ElementName=grid}">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Column1"
                            Binding="{Binding Path=Something1}"
                            Header="Column1" 
                            Width="Auto"  />
        <DataGridTextColumn x:Name="Column2"
                            Binding="{Binding Path=Something2}"
                            Header="Column2"
                            Width="*" />
    </DataGrid.Columns>
</DataGrid>

这里第一列是需要的宽度,第二列是剩下的展开空间。但是 DataGrid 的宽度与它周围的 Grid 的宽度相同,所以达到了想要的结果。

【讨论】:

  • (1) 是的,(2) 是的,但我认为你误解了 (2) —— 内容是内在的,而不是外在的。您的 XAML 代码将 DataGrid 的宽度设置为与 DataGrid OUTSIDE 的 Grid 一样大,反之亦然,将 Grid(和 Window)的大小设置为 DataGrid 所需的大小。
  • HorizontalAlignment="Left" 是我的网格以适应内容,在网格的最后一个网格之后的较早的空白空间......现在解决了
【解决方案2】:

我刚刚遇到了同样的问题,我必须根据标题和单元格的内容在数据网格的列中提供选项以适应其宽度。我使用了以下代码:

private void FitToContent()
    {
        // where dg is my data grid's name...
        foreach (DataGridColumn column in dg.Columns)
        {
            //if you want to size your column as per the cell content
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToCells);
            //if you want to size your column as per the column header
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToHeader);
            //if you want to size your column as per both header and cell content
            column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Auto);
        }
    }

我还提供了一个列选项以适应显示(数据网格的宽度)。为此,我使用了上面相同的代码,并进行了以下小改动:

column.Width = new DataGridLength(1.0, DataGridLengthUnitType.Star);

对于所有列:确保将HorizontalScrollVisibility 保留为Auto

【讨论】:

  • 请注意,这些属性也可以通过 xaml 应用,例如&lt;DataGridTextColumn Width="SizeToHeader" ... /&gt;
  • 您还应该在回答中注意“自动”是宽度的默认值,因此人们不会不必要地用它乱扔代码。 ;)
【解决方案3】:

就我而言,我发现 DataGrid 默认使用 HorizontalAlignment="Stretch" VerticalAlignment="Stretch",所以我将其设置为

<DataGrid ItemsSource="{Binding Source}" Width="Auto" Height="Auto" 
          HorizontalAlignment="Center" VerticalAlignment="Center"/>

然后工作。

【讨论】:

    【解决方案4】:

    Marko's comment 是答案:

    我对你的调整大小逻辑有点不知所措。首先,如果您设置窗口的SizeToContent="WidthAndHeight",那么窗口将显示为数据网格的完整大小。另一方面,如果您以某个预定义的大小显示一个窗口,并且您想在显示窗口后调整窗口的大小,那么此时将大小调整为所需的数据网格是非常不直观的。因为调整大小会从 100px 跳到 400px,但是如果我想调整到只有 250px 怎么办......(假设你通过拖动窗口的角来调整大小)

    【讨论】:

      【解决方案5】:

      SizeToCells 对我有用。我喜欢这个,因为它在 XAML 中而且很简洁。

      <DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}" Header="Name" Width="SizeToCells"/>
      

      【讨论】:

      • SizeToCell 对我在 WPF 中的 DataGridTextColumn 上的 width 属性无效。你使用哪个框架?
      • 我想你把代码的结尾剪掉了 sn -p Width="SizeToCells"。请参阅 DataGridLengthUnitType 枚举帮助。你应该在 4.0 以上。
      【解决方案6】:

      AutoGenarateColumns="False"

      【讨论】:

        猜你喜欢
        • 2021-05-21
        • 1970-01-01
        • 2012-07-22
        • 1970-01-01
        • 2015-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多