关于c#:如何调整WPF DataGrid的大小以适合其内容?

How to resize WPF DataGrid to fit its content?

目的

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

范例(edit-1)

假设您有一个窗口,宽度为100像素,而DataGrid有一列,该单元格为"快速褐狐..."(宽度为400像素)。因此,应该将DataGrid的大小调整为400像素(由于填充,可能会更多),并且将Window的大小也调整为400像素(由于填充也会更多)。

我没有找到任何标准方法来执行此操作(AFAIK WPF提供了将内容剪切为所需宽度的方法,我的问题恰恰相反),因此我想出了这种丑陋的解决方法,效果不太好。

解决方法

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

    我做了几次测试,在某些情况下,计算出的宽度太大(这是一个小问题),在某些情况下,计算出的宽度太小。问题开始于其核心过程-计算TextBox / TextBlock所需的宽度,计算出的宽度总是比其应有的小1个单位(如果我将宽度设置为计算出的宽度,则始终会剪切文本中的1个像素)。

    那么我在这里忽略哪个因素呢?也许更好-已有某种方法来调整DataGrid的大小以适合其内容?

    编码

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        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,这样我的窗口"从不"太小):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
        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;
        }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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


    如果我正确理解您的问题,并且您希望:

  • 一个DataGrid,其中的列与它的最宽内容一样宽。
  • DataGrid适合其内容。
  • 这可以通过数据绑定来实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <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宽度相同,因此可以实现所需的结果。


    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)


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

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

    然后工作。


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

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