如何调整 WPF DataGrid 的大小以适应其内容? [英] How to resize WPF DataGrid to fit its content?

查看:94
本文介绍了如何调整 WPF DataGrid 的大小以适应其内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

宗旨

我想为 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,//可以使用任意文本System.Globalization.CultureInfo.CurrentCulture,小部件.FlowDirection,widget.FontFamily.GetTypefaces().FirstOrDefault(),小部件.字体大小,小部件.前景);返回 formattedText.Width+widget.Padding.Left+widget.Padding.Right;}

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

 public static void AdjustWidthToDataGrid(这个窗口窗口,DataGrid dataGrid,double unknown_factor){var max_widths = dataGrid.Columns.Select(it => window.TextWidth(it.Header as string)* 丑陋的因素).ToArray();foreach(Enumerable.Range(0,dataGrid.Items.Count)中的var行)foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count)){var cell = dataGrid.GetCell(row, col);双倍宽度 = 0;if (cell.Content 是 TextBlock)width = (cell.Content as TextBlock).TextWidth();else if (cell.Content 是 TextBox)width = (cell.Content as TextBox).TextWidth();if (cell.Content 是 FrameworkElement){var widget = cell.Content as FrameworkElement;宽度 = 宽度 + widget.Margin.Left + widget.Margin.Right;}max_widths[col] = Math.Max(max_widths[col],宽度*ugly_factor+cell.Padding.Left+cell.Padding.Right);}双倍宽度差异 = 0;foreach (var col in Enumerable.Range(0, dataGrid.Columns.Count))width_diff += Math.Max(0,max_widths[col] - dataGrid.Columns[col].ActualWidth);如果 (width_diff > 0)window.Width = window.ActualWidth+ width_diff;}

解决方案

Marko 的评论 就是答案:

<块引用>

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

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.

Example (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).

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.

The workaround

  1. iterate over DataGrid headers (assuming they are just strings) and compute width required for the text
  2. 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
  3. sum all differences between DataGrid ActualWidth for columns and the maximum width computed in (2)
  4. increase the window width by the difference computed in (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).

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

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

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

解决方案

Marko's comment is the answer:

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)

这篇关于如何调整 WPF DataGrid 的大小以适应其内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆