如何调整WPF DataGrid的大小以适应其内容?

时间:2011-01-02 10:07:20

标签: c# wpf layout datagrid

目标

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

示例(编辑-1)

假设你有一个100像素宽的窗口,你有一列DataGrid,哪个单元格是“快速棕色狐狸......”(400像素宽)。因此,DataGrid应该调整为400像素(可能更多,因为填充),窗口也应该调整为400像素(由于填充也更多)。

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

解决方法

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

    问题

    我做了几次测试,在某些情况下计算出的宽度太大(这是一个小问题),因为有些情况太小了。问题从它的核心过程开始 - 计算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,这样我的窗口“永远不会”太小):< / p>

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

5 个答案:

答案 0 :(得分:19)

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

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);
        }
    }

另外,我根据显示(datagrid的宽度)提供了一个适合列的选项。因为我使用了上面相同的代码,但有以下小改动:

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

FOR ALL COLUMNS .... :)确保将Horizo​​ntalScrollVisibility保持为Auto。

答案 1 :(得分:8)

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

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

    <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周围的宽度相同,因此可以实现想要的结果。

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

Marko's comment就是答案:

  

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

答案 4 :(得分:1)

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

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

然后工作。