关于System.Drawing.Print名称空间中给类对打印的管理与实现

原创 2004年07月07日 17:30:00
 

关于System.Drawing.Print名称空间中给类对打印的管理与实现<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

一、我们打印时要做的两项工作:

1  绘制要打印的文本:

绘制的工作是在PrintDocumentPrintPage事件的处理函数中进行的。在这个事件的PrintPageEventArgs参数中给我们提供了绘制所必需的Graphics对象和打印的叶面的相关信息。

首先,我们要知道一页可以使用的区域的大小。在PrintPageEventArgs里,MarginBounds中存储了这样的信息。这样我们就可以计算我们在一页当中可以绘制的行数和列数。

int linesPerPage = MarginBounds.Height / lineHeight;

int colsPerPage = MarginBounds.Width / colWidth;

这样我们就可以在打印的循环中使用这些参数判断什么时候一页打印完成。

Int lineCount = 0;

Int colCount = 0;

While (lineCount < linesPerPage)

{

// line drawing codes

       While (colCount < colsPerPage)

{

       // column drawing codes

      

       colCount++;

}

lineCount++;

}

这样,我们可以很轻松的打印出一页的内容。

一页打印完后,如果还有更多的页面需要打印,只要设定PrintPageEventArgs.HasMorePagesture即可。当最后一页绘制完成后再设定这个属性为false就可以中止打印了。

2  调用打印

页面的绘制工作是我们要做的最大的一部分工作。剩下的我们只要去声明一些对象,调用一些函数就可以完成了。

首先,我们声明一个PrintDocument对象。如:

PrintDocument document = new PrintDocument();

然后将我们完成的打印事件处理函数挂接到这个对象上。如:

document.PrintPage += new PrintPageEventHandler(MyPrintPage);

这样,打印的准备工作就做好了。现在只需要在要启动打印的地方调用PrintDocumentPrint()方法就行了。

document.Print();

 

如果你希望有一个打印预览的功能,你只要在你的打印预览的按钮代码中这样写就可以了:

PrintPreviewDialog prevDlg = new PrintPreviewDialog();

prevDlg.Document = document;

prevDlg.ShowDialog();

这样,相关打印的工作就做完了。

二、简单工作的内部实现:

上面,我们知道了在C#中如何实现自己的打印功能。那么,在dotNet中这些是如何实现的呢?这个过程是如何控制的呢?现在我们来谈谈这个。

1  相关的类:

a)       PrintPageEventHandler;

b)       PrintPageEventArgs;

c)       PrintDocument;

d)       PrintControler;

e)       StandardPrintController;(PrintController的子类)

f)       PreviewPrintController; (PrintController的子类)

g)       QueryPageSettingsEventArgs

2  逻辑实现:

当我们实例化一个PrintDocument对象的时候,PrintDocument的建构函数创建一个PrintSettings对象,然后将自己的PrintController对象置为空,最后使用PrintSettings对象创建一个默认的页设置。

当我们调用PrintDocumentPrint方法时,调用PrintController对象的Print方法。在PrintDocument对象的PrintController属性的get方法中,当这个对象为空时,将创建一个StandardPringController对象。PrintDocument对象中调用的就是这样的一个PrintContriller对象。(打印预览中与此不同。当PrintDocument对象传给PrintPreviewDialog对象时,会有一个PreviewPrintController对象被setPrintDocument对象。)

StandardPrintController对象和PreviewPrintController对象的区别在一他们的Graphics对象的创建方式不同。StandardPrintController对象使用打印机设备dc创建Graphics对象。而PreviewPrintController对象则使用MetaFile(基于image)创建一个Graphics对象。

PrintControllerPrint方法中,首先调用自己的OnStartPrint方法设置打印模式。(使用PrintDocument对象中的DefaultPageSettings初始化。)然后调用PrintLoop方法对每一页的打印进行控制。

PrintLoop方法控制了这个打印的循环,这也是我们能够在每页的打印时能够收到一个PrintPage事件的实现之源。

PrintLoop中作的第一项工作是创建一个PrintPageEventArgs对象,并创建一个Graphics对象。这个Graphics对象的创建是调用他的子类overrideOnStartPage方法来完成的。所以,我们的页面绘制到什么样的HDC上将取决于其使用了什么样的PrintController

接着,这个Graphics对象被放置到PrintPageEventArgs对象中。调用OnPrintPage委托。也就是我们的绘制函数。之后,调用OnEndPage完成一个页面的打印。这样就完成了一个页面的绘制。

对于多页的打印,是使用一个do-while来控制的。这个循环是否完成由两个标志来控制。一个是用户是否取消打印工作;另一个是HasMorePages属性的状态。详细的控制见第三部分的函数代码。

3 

三、部分关键函数的代码实现:

1    PrintDocument:

Public class PrintDocument : System.ComponentModel.Component

{

Public PrintDocument()

{

    this.documentName = document;

    this.printerSettings = new PrinterSettings();

    this.printController = null;

    this.defaultPageSettings =

        new PageSettings(this.printerSettings);

}

 

Public void Print()

{

    PrintController controller;

    controller = this.PrintController;

    controller.Print(this);

}

 

protected virtual void OnBeginPrint(PrintEventArgs e)

{

    if(this.beginPrintHandler != null)

        this.beginPrintHandler.Invoke(this, e);

}

}

2    PrintController:

Public class PrintController : System.Object

{

Public PrintController()

{

   this.modeHandle = IntPtr.Zero;

   IntSecurity.SagePrinting.Demand();

}

 

internal void Print(PrintDocument document)

{

    PrintEventArgs args;

    bool flag;

    IntSecurity.SafePrint.Demand();

    Args = new PrintEventArgs();

    Document._OnBeginPrint(args);

   

    if(args.Cancel)

        return;

    this.OnStartPrint(document, args);

    flag = true;

    try

{

    Flag = this.PrintLoop(document);

}

finally

{

    try

    {

        Document._OnEndPrint(args);

        Args.Cancel = (flag | args.Cancel);

        Return;

    }

    finally

    {

        this.OnEndPrint(document, args);

    }

}

}

 

Public void OnStartPrint(PrintDocument document, PrintEventArgs e)

{

    IntSecurity.AllPrintingAndUnmanagedCode.Assert();

    this.modeHandle =

                  document.PrinterSettings.GetHdevmode(

document.DefaultPageSettings);

}

 

Private bool PrintLoop(PrintDocument document)

{

    QueryPageSettingsEventArgs settingArgs;

    PrintPageEventArgs eventArgs;

    Graphics graphics;

    settingArgs = new QueryPageSettingsEventArgs(

(PageSettings)document.DefaultPageSettings.Clone());

 

        do

        {

            Document._OnQueryPageSettings(settingArgs);

            if(settingArgs.Cancel)

                return true;

            eventArgs =

                this.CreatePrintPageEvent(settingArgs.PageSettings);

            graphics = this.OnStartPage(document, eventArgs);

            eventArgs.SetGraphics(graphics);

            try

            {

                document._OpenPrintPage(eventArgs);

                this.OnEndPage(document, eventArgs);

            }

            finally

            {

                eventArgs.Dispose();

            }

        }while( !eventArgs.Cancel && eventArgs.HasMorePages );

}

}

3    StandardPrintController:

Public class StandardPrintController : PrintController

{

Public void OnStartPage(PrintDocument document, PrintPageEventArgs e)

{

    IntPtr ptr1;

    IntPtr ptr2;

    int num1;

    int num2;

    int num3;

    int num4;

    float single1;

    float single2;

    int num5;

    Rectangle rectangle1;

    this.CheckSecurity(document);

    base.OnStartPage(document, e);

    IntSecurity.AllPrintingAndUnmanagedCode.Assert();

    e.PageSettings.CopyToHdevmode(base.modeHandle);

    ptr1 = SafeNativeMethods.GlobalLock(

new HandleRef(this, base.modeHandle));

    try

    {

        ptr2 = SafeNativeMethods.ResetDC(

            new HandleRef(this, this.dc),

            new HandleRef(null, ptr1));

        if (ptr2 == IntPtr.Zero)

        {

             throw new Win32Exception();

        }

 

    }

    finally

    {

         SafeNativeMethods.GlobalUnlock(

new HandleRef(this, base.modeHandle));

    }

    this.graphics = Graphics.FromHdcInternal(this.dc);

    if ((this.graphics != null) && document.OriginAtMargins)

    {

         num1 = UnsafeNativeMethods.GetDeviceCaps(

new HandleRef(null, this.dc), 88);

         num2 = UnsafeNativeMethods.GetDeviceCaps(

new HandleRef(null, this.dc), 90);

num3 = UnsafeNativeMethods.GetDeviceCaps(

new HandleRef(null, this.dc), 112);

num4 = UnsafeNativeMethods.GetDeviceCaps(

new HandleRef(null, this.dc), 113);

         single1 = ((float) ((num3 * 100) / num1));

         single2 = ((float) ((num4 * 100) / num2));

         this.graphics.TranslateTransform(-single1, -single2);

         rectangle1 = e.MarginBounds;

         rectangle1 = e.MarginBounds;

         this.graphics.TranslateTransform(

(float) rectangle1.X, (float) rectangle1.Y);

     }

     num5 = SafeNativeMethods.StartPage(

new HandleRef(this, this.dc));

     if (num5 <= 0)

     {

         throw new Win32Exception();

     }

     return this.graphics;

}

}

4    PreviewPrintController:

Public class Preview class PreviewPrintController : PrintController

{

    public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e)

{

Size size1;

Size size2;

Metafile metafile1;

PreviewPageInfo info1;

Rectangle rectangle1;

this.CheckSecurity();

base.OnStartPage(document, e);

IntSecurity.AllPrintingAndUnmanagedCode.Assert();

e.PageSettings.CopyToHdevmode(base.modeHandle);

rectangle1 = e.PageBounds;

size1 = rectangle1.Size;

size2 = PrinterUnitConvert.Convert(size1, 0, 2);

metafile1 = new Metafile(

this.dc, new Rectangle(0, 0, size2.Width, size2.Height));

info1 = new PreviewPageInfo(metafile1, size1);

this.list.Add(info1);

this.graphics = Graphics.FromImage(metafile1);

if (this.antiAlias)

{

            this.graphics.TextRenderingHint = 4;

this.graphics.SmoothingMode = 4;

}

return this.graphics;

}

}

             

四、总结:

.Net中打印的基本控制逻辑就是这样。当然,这只是我粗略的整理了一下。可能还有很多的细节没有能够分析的很轻的。另外,关于Windows中的WM_PAINT消息是如何和这些控制一起协调工作的,单从我们能看到的这些代码中没有办法去了解。毕竟.Net中真正底层的东西仍然是使用C/C++来开发的。所以这些方面的东西就不太容易得到了。

如果哪位高人有这方面的资料,希望能够提供一下。一边大家能够更好的了解一些.Net的机制。多学习些东西!

C++在命名空间中声明类和成员函数

来源:网络 作者:未知 虽然很多程序员都熟悉名字空间的概念,但他们常常都是被动地使用名字空间。也就是说他们使用的是第三方定义的成员(如标准库的类和函数),而不是在名字空间中声明自己的类和函数。本...
  • guang_jing
  • guang_jing
  • 2014年04月14日 17:10
  • 3244

VS C# 命名空间 “ ”中不存在类型或命名空间名称 “ ” 。是否缺少程序集引用?

命名空间 “ Charge”中不存在类型或命名空间名称 “ DAL” 。是否缺少程序集引用? 这是昨天遇到的问题,解决之后觉得有必要分享出来; 之所以这样报错...
  • u010282984
  • u010282984
  • 2016年08月06日 17:37
  • 3044

x 名称空间中的 Attribute

1. x:class 告诉 XAML 编译器,当 XAML 解析器将包含它的标签解析成 C# 类后,这个类的类名是什么。...
  • GongchuangSu
  • GongchuangSu
  • 2015年08月23日 20:57
  • 380

WPF中的x名称空间

X名称空间中的Attribute Attribute和Property是两个层面的东西: 1)Attribute是语言层面的,是给编译器看的。 2)Property是对象层面的,是给编程逻辑用的。 ...
  • Eric_K1m
  • Eric_K1m
  • 2013年11月11日 17:03
  • 1011

管理表空间中的空间

空间管理在多个级别进行。第一,将空间分配给表空间。这通过设置数据文件的大小来完成。第二,将表空间中的空间分配给段,这通过分配区间来完成。第三,将段中的空间分配给行,这通过维护那些跟踪每个块中的空闲空间...
  • pengpengfly_jhp
  • pengpengfly_jhp
  • 2014年02月18日 21:55
  • 506

面向对象实验四(输入输出流)

一、实验目的 1、掌握用户自定义类型的输入输出。 2、掌握文件的输入、输出。 二、实验内容 1、定义一个复数类,并为其重载>>和 #include using namespace std; /...
  • u012788815
  • u012788815
  • 2013年11月18日 21:52
  • 775

ORA-29857:表空间中存在域索引和/或次级对象

今天ArcGIS的SDE发生了一点小故障,导致系统表丢失,所以需要重建一下SDE数据库,在删除SDE用户和所在的表空间过程中遇到下面两个ORA错误,解决方法如下: 1)删除表空间时报错:ORA-29...
  • kone0611
  • kone0611
  • 2017年07月18日 10:29
  • 353

解决ORA-29857:表空间中存在域索引和/或次级对象 & ORA-01940:无法删除当前连接的用户问题

今天ArcGIS的SDE发生了一点小故障,导致系统表丢失,所以需要重建一下SDE数据库,在删除SDE用户和所在的表空间过程中遇到下面两个ORA错误,解决方法如下: 1)删除表空间时报错:ORA-298...
  • zhaohuihui6628
  • zhaohuihui6628
  • 2015年07月30日 20:13
  • 3181

无法在此分配空间中创建新建卷,因为该磁盘已经包含最大分区数

1. 问题说明       WIN7系统,在系统下创建分区的时候提示“无法在此分配空间中创建新建卷,因为该磁盘已经包含最大分区数”。请问如何处理? 2.分析说明与解决方案 一般用户机器提示这个错...
  • chenbinqq
  • chenbinqq
  • 2014年02月17日 15:40
  • 6751

C++实现三维空间中点到点、点到直线、点到平面的距离计算

C++实现三维空间中点到点、点到直线、点到平面的距离计算 在空间解释几何中,点到点、点到直线、点到平面的距离是基本的计算。计算公式也有多种推导方法(详细参见任何线性代数教材或维基百科)。本文采用向量的...
  • yang_deyuan
  • yang_deyuan
  • 2017年12月14日 17:10
  • 461
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:关于System.Drawing.Print名称空间中给类对打印的管理与实现
举报原因:
原因补充:

(最多只允许输入30个字)