如何在 C# 和 .NET 中打印 DataGrid

        DataGrid 是 .NET 架构中一个功能极其丰富的组件,或许也是最复杂的组件之一。写这篇文章是为了回答“我到底该如何打印 DataGrid 及其内容”这个问题。最初即兴的建议是使用我的屏幕截图文章来截取表单,但这当然无法解决打印 DataGrid 中虚拟显示的无数行数据的问题。后来想了想,这应该很简单,我只需使用 GDI+ 遍历 DataGrid 中的行并打印其内容即可。其实,DataGrid 比这更复杂一些,因为它本身并不包含数据。数据包含在 DataSet 中。因此,最终确定的方法是从 DataGrid 中捕获颜色和字体属性用于打印输出,并从 DataSet 中捕获行中的信息。为了将 DataGridPrinter 的绘制功能封装到 Printer 中,我创建了 DataGridPrinter 类,如下图 2 所示。此类将 DataGrid、PrintDocument 和 DataTable 传递给其构造函数,并利用这些对象将 DataGrid 绘制到打印机。

图 1. Northwind DataGrid 的打印预览

图 2. DataGridPrinter 类 UML 设计(使用 WithClass 2000 进行逆向工程)

        DataGridPrinter 在表单的构造函数中构建,因此它可以被所有打印功能(打印、打印预览等)使用。下面是构建 DataGridPrinter 的代码:

void SetupGridPrinter()  
{  
    dataGridPrinter1 =new DataGridPrinter(dataGrid1, printDocument1,  
    dataSet11.Customers);  
}  

一旦构建了 DataGridPrinter,您就可以通过在 Print Page 事件处理程序中调用其 DrawDataGrid 方法将 DataGrid 绘制到打印机上:

private void printDocument1_PrintPage(object sender,System.Drawing.Printing.PrintPageEventArgs e)  
{  
    Graphics g = e.Graphics;  
    // Draw a label title for the grid  
    DrawTopLabel(g);  
    // draw the datagrid using the DrawDataGrid method passing the Graphics surface  
    bool more = dataGridPrinter1.DrawDataGrid(g);  
    // if there are more pages, set the flag to cause the form to trigger another print page event  
    if (more == true)  
    {  
        e.HasMorePages =true;  
        dataGridPrinter1.PageNumber++;  
    }  
}  

        PrintPage 事件由 PrintDocument 中的 Print 方法和 PrintPreviewDialog 的 ShowDialog 方法触发。下面是窗体将 DataGrid 打印到打印机的方法:

private void PrintMenu_Click(object sender, System.EventArgs e)  
{  
    // Initialize the datagrid page and row properties  
    dataGridPrinter1.PageNumber = 1;  
    dataGridPrinter1.RowCount = 0;  
    // Show the Print Dialog to set properties and print the document after ok is pressed.  
    if (printDialog1.ShowDialog() == DialogResult.OK)  
    {  
        printDocument1.Print();  
    }  
}  

        现在让我们来看看 DataGridPrinter 方法的内部实现。DataGridPrinter 类中有两个主要方法用于执行所有绘制操作:DrawHeader 和 DrawRows。这两个方法都从 DataGrid 和 DataTable 中提取信息来绘制 DataGrid。以下是绘制 DataGrid 行的方法:

public bool DrawRows(Graphics g)  
    {  
        try  
        {  
            int lastRowBottom = TopMargin;  
            // Create an array to save the horizontal positions for drawing horizontal gridlines  
            ArrayList Lines = new ArrayList();  
            // form brushes based on the color properties of the DataGrid  
            // These brushes will be used to draw the grid borders and cells  
            SolidBrush ForeBrush = new SolidBrush(TheDataGrid.ForeColor);  
            SolidBrush BackBrush = new SolidBrush(TheDataGrid.BackColor);  
            SolidBrush AlternatingBackBrush = new SolidBrush  
            TheDataGrid.AlternatingBackColor);  
            Pen TheLinePen = new Pen(TheDataGrid.GridLineColor, 1);  
            // Create a format for the cell so that the string in the cell is cut off at the end of  
            the column width  
            StringFormat cellformat = new StringFormat();  
            cellformat.Trimming = StringTrimming.EllipsisCharacter;  
            cellformat.FormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit;  
            // calculate the column width based on the width of the printed page and the # of  
            columns in the DataTable  
            // Note: Column Widths can be made variable in a future program by playing with the GridColumnStyles of the  
            // DataGrid  
            int columnwidth = PageWidth / TheTable.Columns.Count;  
            // set the initial row count, this will start at 0 for the first page, and be a different  
            value for the 2nd, 3rd, 4th, etc.  
            // pages.  
            int initialRowCount = RowCount;  
            RectangleF RowBounds = new RectangleF(0, 0, 0, 0);  
            // draw the rows of the table   
            for (int i = initialRowCount; i < TheTable.Rows.Count; i++)  
            {  
                // get the next DataRow in the DataTable  
                DataRow dr = TheTable.Rows[i];  
                int startxposition = TheDataGrid.Location.X;  
                // Calculate the row boundary based on teh RowCount and offsets into the page  
                RowBounds.X = TheDataGrid.Location.X; RowBounds.Y = TheDataGrid.Location.Y +  
                 TopMargin + ((RowCount - initialRowCount) + 1) * (TheDataGrid.Font.SizeInPoints +  
                 kVerticalCellLeeway);  
                RowBounds.Height = TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway;  
                RowBounds.Width = PageWidth;  
                // save the vertical row positions for drawing grid lines  
                Lines.Add(RowBounds.Bottom);  
                // paint rows differently for alternate row colors  
                if (i % 2 == 0)  
                {  
                    g.FillRectangle(BackBrush, RowBounds);  
                }  
                else  
                {  
                    g.FillRectangle(AlternatingBackBrush, RowBounds);  
                }  
                // Go through each column in the row and draw the information from the  
                DataRowfor(int j = 0; j < TheTable.Columns.Count; j++)  
                {  
                RectangleF cellbounds = new RectangleF(startxposition,  
                TheDataGrid.Location.Y + TopMargin + ((RowCount - initialRowCount) + 1) *  
                (TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway),  
                columnwidth,  
                TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway);  
                // draw the data at the next position in the row  
                if (startxposition + columnwidth <= PageWidth)  
                {  
                    g.DrawString(dr[j].ToString(), TheDataGrid.Font, ForeBrush, cellbounds, cellformat);  
                    lastRowBottom = (int)cellbounds.Bottom;  
                }  
                // increment the column position  
                startxposition = startxposition + columnwidth;  
            }  
            RowCount++;  
            // when we've reached the bottom of the page, draw the horizontal and vertical grid lines and return true  
        if (RowCount * (TheDataGrid.Font.SizeInPoints + kVerticalCellLeeway) >  
        PageHeight * PageNumber) - (BottomMargin + TopMargin))  
            {  
                DrawHorizontalLines(g, Lines); DrawVerticalGridLines(g, TheLinePen, columnwidth,  
                 lastRowBottom);  
                return true;  
            }  
        }  
// when we've reached the end of the table, draw the horizontal and vertical gridlines and return false  
    DrawHorizontalLines(g, Lines);  
    DrawVerticalGridLines(g, TheLinePen, columnwidth, lastRowBottom);  
    return false;  
}  
catch (Exception ex)  
{  
MessageBox.Show(ex.Message.ToString());  
return false;  
}  

        该方法遍历 DataTable 中的每一行并绘制数据。该方法使用 DataGrid 的属性,用适当的颜色绘制每一行,并使用 DataGrid 的字体绘制每个字符串。如果该方法到达页面底部,则会中断并返回 true,以便将 DataGrid 的剩余部分打印到下一页。

改进:

利用         DataGrid 的 TableStyles 属性中存储的 DataGridColumnStyle 类,可以极大地改进此类。这些属性允许您为某些列指定不同的列宽以及不同的文本对齐方式。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/82197.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言 指针(5)

目录 1.冒泡排序 2.二级指针 3.指针数组 4.指针数组模拟二级数组 1.冒泡排序 1.1 基本概念 冒泡排序&#xff08;Bubble Sort&#xff09; 是一种简单的排序算法&#xff0c;它重复地遍历要排序的数列&#xff0c;一次比较两个元 素&#xff0c;如果它们的顺序错误就把它…

15前端项目----用户信息/导航守卫

登录/注册 持久存储用户信息问题 退出登录导航守卫解决问题 持久存储用户信息 本地存储&#xff1a;&#xff08;在actions中请求成功时&#xff09; 添加localStorage.setItem(token,result.data.token);获取存储&#xff1a;&#xff08;在user仓库中&#xff0c;state中tok…

RSS 2025|斯坦福提出「统一视频行动模型UVA」:实现机器人高精度动作推理

导读 在机器人领域&#xff0c;让机器人像人类一样理解视觉信息并做出精准行动&#xff0c;一直是科研人员努力的方向。今天&#xff0c;我们要探讨的统一视频行动模型&#xff08;Unified Video Action Model&#xff0c;UVA&#xff09;&#xff0c;就像给机器人装上了一个“…

基于论文的大模型应用:基于SmartETL的arXiv论文数据接入与预处理(四)

上一篇介绍了基于SmartETL框架实现arxiv采集处理的基本流程&#xff0c;通过少量的组件定制开发&#xff0c;配合yaml流程配置&#xff0c;实现了复杂的arxiv采集处理。 由于其业务流程复杂&#xff0c;在实际应用中还存在一些不足需要优化。 5. 基于Kafka的任务解耦设计 5.…

Fiori学习专题三十五:Device Adaptation

由于在类似于手机的小面板上显示时&#xff0c;我们为了留出更多空间展示数据&#xff0c;可以将一些控件折叠。 1.修改HelloPanel.view.xml&#xff0c;加入expandable“{device>/system/phone}” expanded"{ !${device>/system/phone} <mvc:ViewcontrollerNam…

【记录】HunyuanVideo 文生视频工作流

HunyuanVideo 文生视频工作流指南 概述 本指南详细介绍如何在ComfyUI中使用腾讯混元HunyuanVideo模型进行文本到视频生成的全流程操作&#xff0c;包含环境配置、模型安装和工作流使用说明。 参考&#xff1a;https://comfyui-wiki.com/zh/install/install-comfyui/install-c…

统一返回JsonResult踩坑

定义了一个统一返回类&#xff0c;但是没有给Data 导致没有get/set方法&#xff0c;请求一直报错 public class JsonResult<T> {private int code;private String message;private T data;public JsonResult() {}public JsonResult(int code, String message, T data) {…

dubbo-token验证

服务提供者过滤器 import java.util.Map; import java.util.Objects;/*** title ProviderTokenFilter* description 服务提供者 token 验证* author zzw* version 1.0.0* create 2025/5/7 22:17**/ Activate(group CommonConstants.PROVIDER) public class ProviderTokenFilt…

沃伦森电气高压动态无功补偿装置助力企业电能优化

在工业生产的复杂电能环境中&#xff0c;电能质量直接影响企业的生产效率和运营成本。XX光伏科技有限公司作为一家快速发展的制造企业&#xff0c;随着生产规模的不断扩大&#xff0c;其内部电网面临功率因数过低、电压波动频繁等问题&#xff0c;导致供电部门罚款增加、设备故…

基于EFISH-SCB-RK3576工控机/SAIL-RK3576核心板的网络安全防火墙技术方案‌(国产化替代J1900的全栈技术解析)

‌基于EFISH-SCB-RK3576/SAIL-RK3576的网络安全防火墙技术方案‌ &#xff08;国产化替代J1900的全栈技术解析&#xff09; ‌一、硬件架构设计‌ ‌流量处理核心模块‌ ‌多核异构架构‌&#xff1a; ‌四核Cortex-A72&#xff08;2.3GHz&#xff09;‌&#xff1a;处理深度…

Maven 动态版本与SNAPSHOT机制详解

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

趣味编程:答案之书

概述&#xff1a;该篇博客主要介绍的是曾经一度风靡全网的答案之书小程序。 目录 1. 效果展示 2. 源码展示 3. 代码逻辑详解 3.1 头文件与全局变量 3.2 main函数 3.3 主循环 3. 4 绘制界面 4. 运行问题 5.小结 1. 效果展示 该小程序是动态的效果&#xff0c; 因此实…

多线程初阶(2)

说到多线程编程&#xff0c;一定少不了线程安全这个话题。我们前面了解了线程的原理以及线程与进程的关系。线程之间共享资源&#xff0c;这就代表了在多线程编程中一定会产生冲突&#xff0c;所以我们需要在敲代码时保证线程安全&#xff0c;避免这样的问题发生。 我们先看一…

【Ubuntu】安裝向日葵远程控制

前言 在Ubuntu 24.04.2下安装向日葵远程控制出错&#xff0c;少了一些依赖&#xff0c;需要安装一些依赖。 1.安装gconf2-common wget http://mirrors.kernel.org/ubuntu/pool/universe/g/gconf/gconf2-common_3.2.6-6ubuntu1_all.deb sudo dpkg -i gconf2-common_3.2.6-6ub…

【Python开源】深度解析:一款高效音频封面批量删除工具的设计与实现

&#x1f3b5; 【Python开源】深度解析&#xff1a;一款高效音频封面批量删除工具的设计与实现 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热情…

JAVA房屋租售管理系统房屋出租出售平台房屋销售房屋租赁房屋交易信息管理源码

一、源码描述 这是一套房屋租售管理源码&#xff0c;基于SpringBootVue框架&#xff0c;后端采用JAVA开发&#xff0c;源码功能完善&#xff0c;涵盖了房屋租赁、房屋销售、房屋交易等业务。 二、源码截图

一篇文章讲清楚mysql的聚簇索引、非聚簇索引、辅助索引

聚簇索引与非聚簇索引最大的区别就是&#xff1a; 聚簇索引的索引和数据是存放在一起的&#xff0c;都是在叶子结点&#xff1b; 非聚簇索引的索引和数据是分开存储的&#xff0c;叶子节点存放的是索引和指向数据文件的地址&#xff0c;通过叶子节点找到索引&#xff0c;再通…

使用ESPHome烧录固件到ESP32-C3并接入HomeAssistant

文章目录 一、安装ESPHome二、配置ESP32-C3控制灯1.主配置文件esp32c3-luat.yaml2.基础通用配置base.yaml3.密码文件secret.yaml4.围栏灯four_light.yaml5.彩灯rgb_light.yaml6.左右柱灯left_right_light.yaml 三、安装固件四、HomeAssistant配置ESPHome1.直接访问2.配置ESPHom…

什么是变量提升?

变量提升&#xff08;Hoisting&#xff09; 是 JavaScript 引擎在代码执行前的一个特殊行为&#xff0c;它会将变量声明和函数声明自动移动到当前作用域的顶部。但需要注意的是&#xff0c;只有声明会被提升&#xff0c;赋值操作不会提升。 ​​核心概念​​ 变量声明提升&…

【万字长文】深入浅出 LlamaIndex 和 LangChain:从RAG到智能体,轻松驾驭LLM应用开发

Langchain系列文章目录 01-玩转LangChain&#xff1a;从模型调用到Prompt模板与输出解析的完整指南 02-玩转 LangChain Memory 模块&#xff1a;四种记忆类型详解及应用场景全覆盖 03-全面掌握 LangChain&#xff1a;从核心链条构建到动态任务分配的实战指南 04-玩转 LangChai…