如何调整WPF数据网格大小以适应其内容?

时间:2022-08-07 21:09:16

The aim

I would like to set such size for the DataGrid (standard, from WPF) so all cells (text) would be fully visible. I have window with DockPanel, with DataGrid in it, so when I resize the window, all nested widgets (DockPanel and DataGrid) are resized accordingly.

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

Example (edit-1)

示例(edit-1)

Let's say you have window, 100 pixels wide and you have DataGrid with one column, which cell is "the quick brown fox..." (400 pixels wide). Thus the DataGrid should be resized to 400 pixels (probably more, because of padding) and the Window should be resized to 400 pixels too (also more, because of padding).

假设有一个100像素宽的窗口,DataGrid有一个列,这个单元格是“quick brown fox…”(400像素)。因此,应该将DataGrid的大小调整为400像素(可能更多,因为填充),窗口也应该调整为400像素(也可能更多,因为填充)。

I didn't find any standard method to do it (AFAIK WPF provides way to clip the content to desired width, my problem is exactly opposite), so I come up with such ugly workaround, which does not work too well.

我没有找到任何标准的方法来完成它(AFAIK WPF提供了一种将内容裁剪到所需宽度的方法,我的问题正好相反),所以我想出了这么难看的解决方案,但效果不是很好。

The workaround

  1. iterate over DataGrid headers (assuming they are just strings) and compute width required for the text
  2. 遍历DataGrid头(假设它们只是字符串)并计算文本所需的宽度
  3. iterate over DataGrid rows per each column (assuming they are TextBlock or TextBox) and compute the maximum width required for the text -- add horizontal paddings for TextBlock/TextBox and horizontal margins for DataGrid cell
  4. 遍历每个列的DataGrid行(假设它们是TextBlock或TextBox),并计算文本所需的最大宽度——为TextBlock/TextBox添加水平的paddings,为DataGrid cell添加水平的空白处
  5. sum all differences between DataGrid ActualWidth for columns and the maximum width computed in (2)
  6. 合计列的数据网格实际宽度与(2)中计算的最大宽度之间的所有差异
  7. increase the window width by the difference computed in (3)
  8. 通过(3)计算的差值增加窗口宽度

THE PROBLEM

I did several tests, and in some cases the computed width is too big (this is minor problem), for some cases is too small. The problem starts at its core procedure -- computing the required width for TextBox/TextBlock, computed width is always 1 unit less than it should be (if I set the width to computed one, 1 pixel from text is always clipped).

我做了几个测试,在某些情况下计算的宽度太大(这是一个小问题),而在某些情况下计算的宽度太小。问题从其核心过程开始——计算文本框/文本块所需的宽度,计算宽度总是小于应有的1单位(如果我将宽度设置为计算1,那么文本中的1像素总是被剪切)。

So which factor I am ignoring here? Or maybe better -- is there already some method to resize DataGrid to fit its content?

我忽略了哪个因子?或者更好——是否已经有一些方法来调整DataGrid的大小以适应它的内容?

The code

Computing width required for text (here for 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;
    }

Adjusting the Window size to fit DataGrid content (ugly_factor is ugly workaround ;-) since I didn't figure out how to fix it properly I set it to 1.3 and this way my window is "never" too small):

调整窗口大小以适应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;
    }

4 个解决方案

#1


17  

I just came out of the same problem where I had to give options in a data grid's column to fit its width as per the content of both header and cell. I used the following code:

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

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

Also I provided an option on columns to fit as per the display (datagrid's width). for that I used the same code above with the following minor change:

我还根据显示(datagrid的宽度)为列提供了一个选项。为此,我使用了上面相同的代码,并做了以下小的改动:

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

列。宽度=新的DataGridLength(1.0, DataGridLengthUnitType.Star);

FOR ALL COLUMNS.... :) Make sure you keep HorizontalScrollVisibility to Auto.

对所有列....:)确保你的HorizontalScrollVisibility能自动显示。

#2


6  

If I understood your question right and you want to:

如果我理解对了你的问题,你想:

  1. A DataGrid where columns are as wide as it's widest content.
  2. 一种数据网格,其中列的宽度与内容的宽度相同。
  3. The DataGrid fits to its contents.
  4. DataGrid符合它的内容。

This can be achieved with databinding:

这可以通过数据库实现:

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

Here the first column is as wide is needed and the second is spreaded space which is left. However the DataGrid's width is the same as Grid's widht that is around it, so the wanted outcome is achieved.

在这里,第一列是需要的宽度,第二列是分散的空间。然而,DataGrid的宽度与围绕它的Grid的widht相同,因此实现了所需的结果。

#3


2  

SizeToCells is what worked for me. I like this because its in XAML and it's concise.

尺寸细胞对我很有用。我喜欢这个,因为它在XAML,它是简洁的。

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

#4


1  

Marko's comment is the answer:

Marko的评论就是答案:

I'm at a bit of a loss with your resizing logic. First of all, if you set the window's SizeToContent="WidthAndHeight", then the window is shown to the full size of the datagrid. On the other hand, if you show a window at some predefined size, and you want to resize the window once the window is shown, then at that point resizing to the datagrid's desired with is very counter intuitive. Because the resizing will jump from say 100px to 400px, but what if I want to resize to only 250px... (asuming you resize by dragging the corner of the window)

你的调整逻辑让我有点不知所措。首先,如果您设置窗口的SizeToContent=“WidthAndHeight”,那么窗口将显示为datagrid的完整大小。另一方面,如果您显示一个预定义大小的窗口,并且希望在显示窗口之后调整窗口的大小,那么在这一点上,使用datagrid的大小是非常反直观的。因为调整大小会从100px跳到400px,但是如果我想要调整到250px呢?(通过拖动窗口的一角来调整大小)

#1


17  

I just came out of the same problem where I had to give options in a data grid's column to fit its width as per the content of both header and cell. I used the following code:

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

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

Also I provided an option on columns to fit as per the display (datagrid's width). for that I used the same code above with the following minor change:

我还根据显示(datagrid的宽度)为列提供了一个选项。为此,我使用了上面相同的代码,并做了以下小的改动:

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

列。宽度=新的DataGridLength(1.0, DataGridLengthUnitType.Star);

FOR ALL COLUMNS.... :) Make sure you keep HorizontalScrollVisibility to Auto.

对所有列....:)确保你的HorizontalScrollVisibility能自动显示。

#2


6  

If I understood your question right and you want to:

如果我理解对了你的问题,你想:

  1. A DataGrid where columns are as wide as it's widest content.
  2. 一种数据网格,其中列的宽度与内容的宽度相同。
  3. The DataGrid fits to its contents.
  4. DataGrid符合它的内容。

This can be achieved with databinding:

这可以通过数据库实现:

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

Here the first column is as wide is needed and the second is spreaded space which is left. However the DataGrid's width is the same as Grid's widht that is around it, so the wanted outcome is achieved.

在这里,第一列是需要的宽度,第二列是分散的空间。然而,DataGrid的宽度与围绕它的Grid的widht相同,因此实现了所需的结果。

#3


2  

SizeToCells is what worked for me. I like this because its in XAML and it's concise.

尺寸细胞对我很有用。我喜欢这个,因为它在XAML,它是简洁的。

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

#4


1  

Marko's comment is the answer:

Marko的评论就是答案:

I'm at a bit of a loss with your resizing logic. First of all, if you set the window's SizeToContent="WidthAndHeight", then the window is shown to the full size of the datagrid. On the other hand, if you show a window at some predefined size, and you want to resize the window once the window is shown, then at that point resizing to the datagrid's desired with is very counter intuitive. Because the resizing will jump from say 100px to 400px, but what if I want to resize to only 250px... (asuming you resize by dragging the corner of the window)

你的调整逻辑让我有点不知所措。首先,如果您设置窗口的SizeToContent=“WidthAndHeight”,那么窗口将显示为datagrid的完整大小。另一方面,如果您显示一个预定义大小的窗口,并且希望在显示窗口之后调整窗口的大小,那么在这一点上,使用datagrid的大小是非常反直观的。因为调整大小会从100px跳到400px,但是如果我想要调整到250px呢?(通过拖动窗口的一角来调整大小)