CSDN博客

img virginsoldier

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

发表于2004/7/7 17:30:00  1084人阅读

分类: DotNet

 

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

 

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

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的机制。多学习些东西!

0 0

相关博文

我的热门文章

img
取 消
img