You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF如何实现点击按钮打印DataGrid全量内容并自动分页

问题根因

默认状态下DataGrid开启了UI虚拟化,仅渲染当前屏幕可视区域内的行元素,可视区域外的行根本没有生成对应的可视化实例,直接调用PrintVisual抓取控件画面打印,自然只能拿到当前屏幕显示的内容;而且直接打印可视化对象的逻辑不会自动处理分页,超出单页打印区域的内容会被直接裁切。

实现步骤
  • 打印触发后先临时关闭DataGrid的行虚拟化,强制控件渲染所有行的可视化内容
    对应代码如下,必须调用UpdateLayout()触发即时渲染,否则拿不到全量行元素:
    dataGrid.EnableRowVirtualization = false;
    dataGrid.UpdateLayout();
    
  • 基于FixedDocument构造可自动分页的打印文档,不要直接用PrintVisual打印DataGrid控件本身
    核心逻辑是逐行测量渲染完成的DataGrid行高,累计高度达到单页可打印区域阈值时自动新建页面,剩余内容顺延到新页,同时每页可以保留表头方便阅读。核心实现代码:
    PrintDialog printDialog = new PrintDialog();
    if (printDialog.ShowDialog() != true) return;
    
    // 临时关闭虚拟化渲染全量行
    dataGrid.EnableRowVirtualization = false;
    dataGrid.UpdateLayout();
    
    // 初始化固定文档,设置页面尺寸和打印区域一致
    FixedDocument fixedDoc = new FixedDocument();
    fixedDoc.DocumentPaginator.PageSize = new Size(printDialog.PrintableAreaWidth, printDialog.PrintableAreaHeight);
    double pageMargin = 24;
    double usablePageWidth = printDialog.PrintableAreaWidth - pageMargin * 2;
    double usablePageHeight = printDialog.PrintableAreaHeight - pageMargin * 2;
    
    // 初始化第一页
    PageContent currentPageContent = new PageContent();
    FixedPage currentPage = new FixedPage
    {
        Width = printDialog.PrintableAreaWidth,
        Height = printDialog.PrintableAreaHeight
    };
    double currentUsedHeight = 0;
    
    // 构造通用表头
    List<TextBlock> headerElements = new List<TextBlock>();
    foreach (var col in dataGrid.Columns)
    {
        headerElements.Add(new TextBlock
        {
            Text = col.Header?.ToString() ?? string.Empty,
            Width = col.ActualWidth,
            FontWeight = FontWeights.Bold
        });
    }
    StackPanel pageHeader = new StackPanel
    {
        Orientation = Orientation.Horizontal,
        Margin = new Thickness(pageMargin, pageMargin, pageMargin, 0)
    };
    headerElements.ForEach(h => pageHeader.Children.Add(h));
    currentPage.Children.Add(pageHeader);
    currentUsedHeight += pageHeader.ActualHeight + 12;
    
    // 逐行填充内容
    for (int i = 0; i < dataGrid.Items.Count; i++)
    {
        DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
        if (row == null) continue;
        
        // 强制测量行尺寸
        row.Measure(new Size(usablePageWidth, double.PositiveInfinity));
        row.Arrange(new Rect(new Point(0,0), row.DesiredSize));
    
        // 当前页剩余空间放不下当前行时,封存当前页,新建下一页
        if (currentUsedHeight + row.DesiredSize.Height > usablePageHeight)
        {
            ((IAddChild)currentPageContent).AddChild(currentPage);
            fixedDoc.Pages.Add(currentPageContent);
    
            // 重置新页状态
            currentPageContent = new PageContent();
            currentPage = new FixedPage
            {
                Width = printDialog.PrintableAreaWidth,
                Height = printDialog.PrintableAreaHeight
            };
            currentUsedHeight = 0;
    
            // 新页追加表头
            StackPanel newPageHeader = new StackPanel
            {
                Orientation = Orientation.Horizontal,
                Margin = new Thickness(pageMargin, pageMargin, pageMargin, 0)
            };
            headerElements.ForEach(h => newPageHeader.Children.Add(new TextBlock
            {
                Text = h.Text,
                Width = h.Width,
                FontWeight = FontWeights.Bold
            }));
            currentPage.Children.Add(newPageHeader);
            currentUsedHeight += newPageHeader.ActualHeight + 12;
        }
    
        // 把行添加到当前页指定位置
        FixedPage.SetLeft(row, pageMargin);
        FixedPage.SetTop(row, currentUsedHeight);
        currentPage.Children.Add(row);
        currentUsedHeight += row.DesiredSize.Height;
    }
    
    // 把最后一页加入文档
    ((IAddChild)currentPageContent).AddChild(currentPage);
    fixedDoc.Pages.Add(currentPageContent);
    
    // 提交打印任务
    printDialog.PrintDocument(fixedDoc.DocumentPaginator, $"DataGrid数据打印_{DateTime.Now:yyyyMMddHHmm}");
    
    // 打印完成恢复行虚拟化,避免大数据量下UI卡顿
    dataGrid.EnableRowVirtualization = true;
    
  • 打印任务提交后立刻恢复DataGrid的虚拟化配置,全量渲染行元素会占用较高内存,数据量较大时长期关闭虚拟化会导致UI操作卡顿。
踩坑提示
  • 如果DataGrid使用了自定义单元格模板,逐行测量前要确保模板内的可视化元素完成加载,否则行高计算会偏小,出现内容截断问题。
  • 不要用截图拼接的方案实现打印,输出的位图内容放大后会模糊,也不支持文字选中复制;基于FixedDocument输出的是矢量内容,打印清晰度和屏幕显示效果完全一致。
  • 单表数据量超过10万行时,临时渲染全量行会有几百毫秒到几秒的卡顿,可以在打印触发时加个简单的加载遮罩,避免用户误判程序无响应。

内容的提问来源于stack exchange,提问作者Hulises

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 17:39:22