在 DotNet Framework 上打印 DataGridView






4.71/5 (24投票s)
此库在 .NET Framework 3.5 中提供 DataGridView 打印功能(C# VB F#)。
GridDrawer.Net
目录
演示
DataGridView 控件是一个非常有用的工具,可以以表格格式显示数据。然而,该控件并未内置打印方法,这使得开发人员需要创建或设计一种方法来轻松打印 DataGridView 的内容。
DataDrawer 是一个类 DLL,可以轻松提供 DataGridView 打印功能。该库是用 C# 编写的,有一个针对 .NET 3.5 的版本,集成了 LINQ,还有一个 .NET 2.0 版本。该库支持 C++、C#、VB.NET 和 F# 开发环境。
 
 
引言
类库 DLL 为开发人员提供了一种无缝的方法来实现 DataGridView 打印。
DLL 实现了以下功能:- 按级别或列打印
- 打印选定的行或列
- 将分区居中到打印纸上
- 页码
- 优化的 DataGridViewTextBoxCell 打印
- 如 DataGridView 控件所示的自动换行和单元格大小
- 利用 .NET Framework 3.5 的强大功能。也提供 .NET 2.0 版本。
- 提供缩放调整以强制列适应单张纸
- 可选的页眉、页脚和标题块打印
- 自包含的库,方便开发人员使用
- 兼容 C++、C#、VB.NET 和 F#
代码行
为了使打印作业顺利进行,一个基本思想是在项目中创建一条代码线。
DataGridView 有多个级别,分为多个分区。级别标识可在纸张上打印的行数。分区是可在纸张上打印的列数。
有了这个概念,就可以很容易地进行一些计算,并拥有一组覆盖 DataGridView 的分区。结构
 
 
实现
类 DataGridViewExtentions
|  | 此类实现了打印作业,并为 DataGridView 及其组件添加了功能。 Font、ColumnWidth、HeaderHeight 和 RowHeight 函数有一些重载,它们使用“scale”参数。“scale”参数是“FitColumnsToPage”功能的关键。 代码设计旨在为函数的形式提供统一或相似的方法。 public static Color ForeColor(this DataGridViewCell cell)
{
    if (cell.HasStyle && cell.Style.ForeColor != Color.Empty) return cell.Style.ForeColor;
    else return cell.InheritedStyle.ForeColor;
}
public static Color BackColor(this DataGridViewCell cell)
{
    if (cell.HasStyle && cell.Style.BackColor != Color.Empty) return cell.Style.BackColor;
    else return cell.InheritedStyle.BackColor;
}
    使用这种编码方法,当不需要时,应最小化初始化样式。请参阅 MSDN 上的 Windows Forms DataGridView 控件中的单元格样式。 | 
类 DocumentMetrics
|  | 用于跟踪文档的可打印坐标; public static DocumentMetrics FromPrintDocument(PrintDocument printDocument)
{
    PageSettings pageSettings = printDocument.DefaultPageSettings;
    
    return new DocumentMetrics()
    {
        Width = 
            (pageSettings.Landscape)
                ?pageSettings.PaperSize.Height:pageSettings.PaperSize.Width,
        Height = 
            (pageSettings.Landscape)
                ?pageSettings.PaperSize.Width:pageSettings.PaperSize.Height,
        LeftMargin = pageSettings.Margins.Left,
        TopMargin = pageSettings.Margins.Top,
        RightMargin = pageSettings.Margins.Right,
        BottomMargin = pageSettings.Margins.Bottom
    };
}
         | 
PartitionBounds 类
|   | PartitionBounds 类包含从 DataGridView 分割的分区数量的边界,即分区的包含的行和列以及边界的坐标信息(大小)。此类可以命名为 PartitionMetrics,但可能与 DocumentMetrics 混淆。 | 
Partition 类
|   | Partition 类表示可在单张纸上打印的 DataGridView 的部分。 // code showing how rows are returned by this class
public DataGridViewRow GetRow(int i)
{
    return GridView.Rows[Bounds.StartRowIndex + i];
}
public IEnumerable | 
PrintBlock 抽象类
 
 
此类将标题、页眉和页脚的打印与库隔离开来。
GridDrawer 中定义了 3 个 PrintBlock 对象:TitlePrintBlock、SheetFooter 和 SheetHeader。
这些 PrintBlock 对象是通过扩展 PrintBlock 类来定义的。
下面描述了使用此库实现标题、页眉和页脚打印的方法。首先调用 GetSize 方法,该方法设置一个 Rectangle,Draw 方法将在其中进行打印。这使我们能够定义一些要打印的块,而无需修改库核心。
 
 
Draw 函数接收一个 Dictionary 参数,其中包含有关页码、总页数、日期和时间的 CodeEnum 枚举信息。
注意。 Lib.GridDraw.Tools 中已实现一个可重用的 TitlePrintBlock 类。有关详细信息,请参阅“使用此库”部分。
public class TitlePrintBlock : PrintBlock
{
    public String Title { get; set; }
    public Color ForeColor { get; set; }
    public Font Font { get; set; }
    public StringFormat Format { get; set; }
    public override SizeF GetSize(Graphics g, DocumentMetrics metrics)
    {
        return g.MeasureString(Title, Font, metrics.PrintAbleWidth, Format);
    }
    public override void Draw(Graphics g, Dictionary codes)
    {
        g.DrawString(Title, Font, new SolidBrush(ForeColor), Rectangle, Format);
    }
}    
 
GridDrawer 类
该库的核心是 GridDrawer 类。GridDrawer 公开了类的属性和方法。使用此类应该非常直接,并且可以轻松地集成到您的代码中。
 
 该类有三个主要轴。
计算分区边界
- 打印文档时的第一步是计算要打印的纸张的分区边界。必须注意标题(第一级)、页眉和页脚的高度。
- 如果将“MustFitColumnsToPage”设置为 true 且列宽大于纸张的可打印宽度,则计算比例。
- 定义必须出现在分区第一行的行。
- 定义必须出现在分区第一列的列。
- 定义一个代码 Dictionary,为标题、页眉和页脚打印、页码、总页数、日期和时间信息设置 PrintBlock 抽象类。
创建分区
所有分区都在单个实例中计算和创建。分区根据初始化过程或步骤中计算的信息进行设置。
绘制纸张
根据分区信息,绘制纸张。- 页眉(可选)
- 标题(可选)
- 分区(列标题和分区的单元格)
- 页脚(可选)
使用库
需要页眉?
创建一个扩展 PrintBlock 类的类,类似于演示项目中包含的类。public class HeaderPrintBlock : PrintBlock
{
    float imgHeight = 75;
    public override SizeF GetSize(Graphics g, DocumentMetrics metrics)
    {
        return new SizeF(metrics.PrintAbleWidth, imgHeight + 2); //+2 for spacing with document
    }
    public override void Draw(System.Drawing.Graphics g, Dictionary codes)
    {
        GraphicsUnit units = GraphicsUnit.Pixel;
        RectangleF rec = Properties.Resources.logo.GetBounds(ref units);
        
        float scale = imgHeight / rec.Height;
        // as you can see below, we are using the base.Rectangle.property which has been set by GridDrawer Class
        // after it knows The Size of this block.
        g.DrawImage(Properties.Resources.logo, new RectangleF(Rectangle.X, Rectangle.Y, rec.Width * scale, imgHeight));
    }
}
 
需要页脚?
创建一个扩展 PrintBlock 类的类,类似于演示项目中包含的类。    public class FooterPrintBlock : PrintBlock
    {
        Font font = new Font("Tahoma", 9, GraphicsUnit.Point);
        public override SizeF GetSize(Graphics g, DocumentMetrics metrics)
        {
            return g.MeasureString("Page X Of Y", font);
        }
        public override void Draw(System.Drawing.Graphics g, Dictionary codes)
        {
            StringFormat format = new StringFormat();
            format.Trimming = StringTrimming.Word;
            format.FormatFlags = StringFormatFlags.NoWrap;
            format.Alignment = StringAlignment.Far;
            // as you can see below, we are using the codes param to know on which page we are for instance.
            g.DrawString(
                string.Format("Page {0} Of {1}", codes[CodeEnum.SheetNumber], codes[CodeEnum.SheetsCount]),
                font,
                new SolidBrush(Color.Black),
                Rectangle,
                format);
        }
    }
 
需要标题?
初始化 Lib.GridDraw.Tools.TitlePrintBlock.cs 中实现的重用类。TitlePrintBlock titleBlock = new TitlePrintBlock(printDocument.DocumentName,Color.DarkBlue);
最后一步:打印 DataGridView
Lib.GridDraw.Tools.PrintingDataGridViewProvider 类使用 printDocument.PrintPage 事件初始化 printProvider,以管理 DataGridview 的打印。printProvider = Tools.PrintingDataGridViewProvider.Create(
    printDocument,
    GridView, chkCenter.Checked, chkFitColumns.Checked,
    new TitlePrintBlock(printDocument.DocumentName,Color.DarkBlue),
    new PrintBlocks.HeaderPrintBlock(),
    new PrintBlocks.FooterPrintBlock());
待办事项
- 按级别打印与按列打印(在 版本 1.0.0.2 的 Change Set 10625 中可用)
- 仅打印选定的行和列(在 版本 1.0.0.3 的 Change Set 10805 中可用)
- 维护 .Net 2 项目(在 版本 1.0.0.3 的 Change Set 10868 中可用)
- 为每行提供一个打印块(评论或投票)
- 提供一种广泛的方式来打印每种类型的 DataGridViewCell(评论或投票)
结论
希望您喜欢 GridDraw.NET。请随时提供您的反馈。很高兴能创建并为社区贡献这个项目。
关注点
这是我用 .NET 3.5 和 Linq 编写的第一个项目。理解和使用 Linq 简化了源代码。
谢谢
我谨感谢 Salan Al-Ani 撰写的 CodeProject 文章。
并感谢 codeplex.com 为 GridDrawer 库提供源代码控制功能。
我还要感谢 Marc Miller,他审阅了本文的许多部分并纠正了大部分语法错误。


