65.9K
CodeProject 正在变化。 阅读更多。
Home

在 DotNet Framework 上打印 DataGridView

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.71/5 (24投票s)

2008年4月27日

Eclipse

6分钟阅读

viewsIcon

408539

downloadIcon

21334

此库在 .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# 开发环境。

Demo GridDrawer.Net

引言

类库 DLL 为开发人员提供了一种无缝的方法来实现 DataGridView 打印。

DLL 实现了以下功能:
  • 按级别或列打印
  • 打印选定的行或列
  • 将分区居中到打印纸上
  • 页码
  • 优化的 DataGridViewTextBoxCell 打印
  • 如 DataGridView 控件所示的自动换行和单元格大小
  • 利用 .NET Framework 3.5 的强大功能。也提供 .NET 2.0 版本。
  • 提供缩放调整以强制列适应单张纸
  • 可选的页眉、页脚和标题块打印
  • 自包含的库,方便开发人员使用
  • 兼容 C++、C#、VB.NET 和 F#

代码行

为了使打印作业顺利进行,一个基本思想是在项目中创建一条代码线。

DataGridView 有多个级别,分为多个分区。级别标识可在纸张上打印的行数。分区是可在纸张上打印的列数。

有了这个概念,就可以很容易地进行一些计算,并拥有一组覆盖 DataGridView 的分区。

结构

Project Structure

实现

类 DataGridViewExtentions

DataGridViewExtentions Class

此类实现了打印作业,并为 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

DocumentMetrics Class

用于跟踪文档的可打印坐标;
为了做到这一点,“FromPrintDocument”函数基于给定的 PrintDocument 参数构建一个新的 PrintDocument。此方法用于将打印属性与库解耦或分离。

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 Class

PartitionBounds 类包含从 DataGridView 分割的分区数量的边界,即分区的包含的行和列以及边界的坐标信息(大小)。此类可以命名为 PartitionMetrics,但可能与 DocumentMetrics 混淆。

Partition 类

Partition Class

Partition 类表示可在单张纸上打印的 DataGridView 的部分。
此类封装了 PartitionBounds 类,并提供了易于检索要打印的分区所分配的 DataGridViewColumns 和 DataGridViewRows 的函数。

// code showing how rows are returned by this class
public DataGridViewRow GetRow(int i)
{
    return GridView.Rows[Bounds.StartRowIndex + i];
}

public IEnumerable GetRows()
{
    for (int i = 0; i < Bounds.RowsCount; i++)
        if (GetRow(i).Visible) yield return GetRow(i);
}
        

PrintBlock 抽象类

PrintBlock Abstract class

此类将标题、页眉和页脚的打印与库隔离开来。
GridDrawer 中定义了 3 个 PrintBlock 对象:TitlePrintBlock、SheetFooter 和 SheetHeader。
这些 PrintBlock 对象是通过扩展 PrintBlock 类来定义的。

下面描述了使用此库实现标题、页眉和页脚打印的方法。首先调用 GetSize 方法,该方法设置一个 Rectangle,Draw 方法将在其中进行打印。这使我们能够定义一些要打印的块,而无需修改库核心。

CodeEnum enumeration

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 公开了类的属性和方法。使用此类应该非常直接,并且可以轻松地集成到您的代码中。

GridDrawer Class
该类有三个主要轴。

计算分区边界

  1. 打印文档时的第一步是计算要打印的纸张的分区边界。必须注意标题(第一级)、页眉和页脚的高度。
  2. 如果将“MustFitColumnsToPage”设置为 true 且列宽大于纸张的可打印宽度,则计算比例。
  3. 定义必须出现在分区第一行的行。
  4. 定义必须出现在分区第一列的列。
  5. 定义一个代码 Dictionary,为标题、页眉和页脚打印、页码、总页数、日期和时间信息设置 PrintBlock 抽象类。

创建分区

所有分区都在单个实例中计算和创建。分区根据初始化过程或步骤中计算的信息进行设置。

绘制纸张

根据分区信息,绘制纸张。
  1. 页眉(可选)
  2. 标题(可选)
  3. 分区(列标题和分区的单元格)
  4. 页脚(可选)

使用库

需要页眉?

创建一个扩展 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());

待办事项

结论

希望您喜欢 GridDraw.NET。请随时提供您的反馈。很高兴能创建并为社区贡献这个项目。

关注点

这是我用 .NET 3.5 和 Linq 编写的第一个项目。理解和使用 Linq 简化了源代码。

谢谢

我谨感谢 Salan Al-Ani 撰写的 CodeProject 文章。
并感谢 codeplex.com GridDrawer 库提供源代码控制功能。
我还要感谢 Marc Miller,他审阅了本文的许多部分并纠正了大部分语法错误。

© . All rights reserved.