• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
云飞扬
大风起兮云飞扬
博客园    首页    新随笔    联系   管理    订阅  订阅
1.2.3 MFC的打印功能分析
1.2.3 MFC的打印功能分析

1.2.3 MFC的打印功能分析
 
 1.框架中的打印
 MFC的框架内置了功能强大的打印和打印预览功能。首先,分析MFC应用程序框架打印的内在机制,
 这样将能更有效地使用打印机。
 
 在视图类及其派生类中,通过重载OnDraw(CDC*pDC)函数,利用它提供的pDC(设备上下文)指针,可以在
 屏幕上显示各种图形和数据。CView类的打印是通过OnPrint(CDC *pDC,CPrintInfo*pInfo)这个函数实现
 的,OnPrint()函数对打印的实现就是简单的调用OnDraw(CDC*pDC)这个函数,把打印机的设备上下文
 指针pDC传递给OnDraw(CDC *pDC)函数。可见CView类对输出到屏幕和输出到打印机的处理都是一样的,
 只是换了一个设备上下文而已(输出到屏幕时是通过OnPaint()函数实现的)。
 
 如果重载OnPrint()函数,可以选择根本不调用OnDraw来支持打印逻辑。OnPrint有两个参数,一个是指向
 打印机设备环境的指针,一个是打印信息对象CPrintInfo的指针,该信息包括纸张大小、当前页码和最大
 页码。对每个需要打印的页,应用程序框架都要调用一次OnPrint(),在OnPrintInfo结构中记录着当前页码。
 
 视图类及其派生类在进行显示和打印之前都会调用virtual void OnParepareDC(CDC *pDC,CPrintInfo* pInfo = null)
 这个虚成员函数来准备设备上下文,可以重载这个虚成员函数,进行坐标转换。下面是MFC框架打印的基本流程:
 
    OnPreparePrint---->设置起始和终止页 
       |
    OnBeginPrinting---->创建GDI对象
       |
    OnPrepareDC(每页)---->设置映射模式,并选择检测打印任务的结尾
       |
    OnPrint(每页)----->打印输出
       |
    是否打印完毕
       |
       y
    OnEndPrinting--->删除GDI对象
 
 CDC::GetDeviceCaps 获得指定设备的信息。
 int GetDeviceCaps(int nIndex)const;
 返回值:如果成功,则返回需要的能力值。
 LOGPIXELSX:沿显示宽度方向,每一逻辑单位的像素数。
 LOGPIXELSY:沿显示高度方向,每一逻辑单位的像素数。
 
 
 
 void CPrintProjView::OnDraw(CDC *pDC)
 {
   pDC->TextOut(400,200,_T("Hello word"));
 }
 
 BOOL CPrintProjView::OnPreparePrinting(CPrintInfo*pInfo)
 {
    pInfo->SetMaxPage(2);
    return DoPreparePrinting(pInfo);
 }
 
 void CPrintProjView::OnPrepareDC(CDC *pDC,CPrintInfo *pInfo)
 {
    CView::OnPrepareDC(pDC,pInfo);
    pDC->SetMapMode(MM_ANISOTROPIC); //转换坐标映射方式
    CSize size = CSize(800,560);
    pDC->SetWindowExt(size);//确定窗口的大小
    //得到实际设备每逻辑英寸的像素数量
    int xLogPixelPerInch = pDC->GetDeviceCaps(LOGPIXELSX);
    int yLogPixelPerInch = pDC->GetDeviceCaps(LOGPIXELSY);
   
    //得到设备坐标和逻辑坐标的比例
    long xExt = (long)size.cx * xLogPixelPerInch / 96;
    long yExt = (long)size.cy * yLogPixelPerInch / 96;
    pDC->SetViewportExt((int)xExt,(int)yExt); //设置视口大小
 }
 
 在上面的程序中,首先将坐标映射方式改变为MM_ANISOSTROPIC方式,即各向异性的意思,
 在这种坐标方式下,x轴和y轴的逻辑单位可以进行任意的缩放。改变坐标映射方式后,就要
 确定窗口大小和视口大小,注意窗口大小就是我们在屏幕上所见的尺寸,,而视口大小是实际
 设备,如打印机等,和显示设备每逻辑英寸的像素数量比较所得的比例尺.
 
 通过函数得到显示器和打印机每逻辑英寸的像素数量,然后对视口大小进行相应的缩放,就可以
 使屏幕上的显示和打印机的输出是一致了.
 
 编译、运行程序,使用打印预览功能,会看到在两个视图和预览两个页面中"Hello word"的位置是
 一样的。
 
 下次在看的时候,将这一部分再串一下。注意下面几点。
 1、只有在MM_ANISOTROPIC和MM_ISOTROPIC映射模式下,SetWindowExt和SetViewportExt才起作用。
    MM_ANISOTROPIC和MM_ISOTROPIC的区别是:
    MM_ANISOTROPIC是不锁定纵横比,也就是x和y各向异性,x和y轴的逻辑单位可以任意缩放。
    MM_ISOTROPIC:是锁定纵横比,x和y轴的比值是1:1

   
 2、SetWindowExt()设置窗口的x和y轴范围,也就是窗口的大小
    SetViewportExt()设置视口的x和y轴范围,也就是视口的大小
 
 3、窗口大小是指:我们在屏幕上所见的尺寸
    视口大小是指:实际设备,如打印机等.

   
2、框架之外的打印
   框架实现了对打印的一些底层支持,直接的打印机制是通过函数StartDoc和EndDoc()来实现的。应用程序
   要使用打印机时,它首先使用CreateDC或PrintDlg来获取指向打印机设备环境的一个句柄,这就使得打印机
   设备驱动程序库模块被加载到内存(如果还没有加载到内存的话),并进行初始化。然后,程序调用StartDoc
   函数,通知一个新文档开始了。StartDoc函数是由GDI模块来处理的。GDI模块调用打印机设备驱动程序中的
   control函数告诉打印机准备打印。
  
   打印一个文档的过程以StartDoc调用开始,以EndDoc调用结束。调用StartPage来开始一页,调用EndPage来
   结束该页。

  
   下面这段代码在对话框中实现了对打印的支持。
   /*
   GetPrinterDC 获取设备环境的句柄。
   HDC GetPrinterDC()const;
   返回值:
   如果成功则返回一个打印机设备环境的句柄;否则返回null.
   说明:
   如果CPrintDialog构造函数的参数bPrintSetupOnly是FALSE(表明显示的是Print对话框,则GetPrinterDC返回一个
   打印机设备环境句柄。当你使用完这个设备环境时,你必须调用Windows DeleteDC函数来删除它。)
   */
   /*
   {
     long cbSize,
     CString lpszDocName,
     CString lpszOutput
   } DOCINFO;
   对文档进行定义的一个结构。
   cbSize:结构的大小
   lpszDocName:文档的名字
   lpszOutput:输出文档的名字
   */
   /*
   StartDoc:开始新的打印作业
   CDC::StartDoc
   int StartDoc(LPDOCINFO lpDocInfo);
   返回值:如果出错,例如存储空间不足或指定端口无效,则返回-1否则返回正值。
   参数:
   lpDocInfo: DOCINFO结构的指针。该结构包含了文档文件和输出文件的名字。
  
   说明:
   通知设备的驱动程序开始一个新的打印作业,其后所有的StartPage和EndPage调用处于假
   脱机状态,直到EndDoc调用出现。这确保了长于一页的文档不被其它作业中断。  
   */
   /*
   EndDoc  结束由StartDoc成员函数启动的打印作业。
   CDC::EndDoc
   int EndDoc();
   返回值:
   如果成功,则返回值大于零或等于零,出错则返回值小于零。下面列出了一般的错误
   类型:
   SP_ERROR:一般错误。
   SP_OUTOFDISK:假脱机所需的磁盘空间不足,没有其它可用的磁盘空间。
   SP_OUTOFMEMORY:假脱机所需的内存不足。
   SP_USERABORT: 用户在打印管理中中止作业。
  
   说明:
   中止由StartDoc成员函数调用的打印作业。在成功完成打印作业后应立即调用。
   如果应用遇到打印错误或取消的打印操作,决不可用EndDoc或AbortDoc去中止
   操作,GDI在返回错误值之前自动中止操作。
  
   */
   /*
   StartPage:通知设备的驱动程序开始新页。
   CDC::StartPage
   int StartPage
   说明:
   调用该成员函数使用打印机驱动程序做好准备接收数据。在StartPage和EndPage之间,
   ResetDC成员函数不起作用。
   */
   /*
   EndPage:通知打印机驱动程序打印页结束。
   CDC::EndPage
   int EndPage()
   返回值:如果成功,则返回大于或等于零的值,如果失败则返回如下错误类型:
   SP_ERROR:一般错误
   SP_APPABORT: 作业终止
   SP_USERABORT:用户在打印管理中中止作业。
   SP_OUTOFDISK:假脱机所需的磁盘空间不足。
   SP_OUTOFMEMORY:假脱机所需的内存不足。
  
   说明:
   通知设备已经写完一页。该成员函数通常用在打印机驱动程序开始新的一页。
   */
   /*
   AbortDoc:终止当前打印任务,擦除自上次调用StartDoc成员函数以业写入设备的任何内容。
   CDC::AbortDoc
   int AbortDoc();
   返回值:如果成功,则返回大于或等于零的值,如果出现错误,则为负值。与EndPage和
   EndDoc返回的错误类型值一样。
   说明:
   终止当前打印任务,并擦除自上次StartDoc以后写入设备的任何任务。
   */
  
   /*
   CPrintInfo没有基类。
   CPrintInfo存储有关一次打印或打印预览的信息。每次选择Print或PrintPreview命令,框架
   就会创建一个CPrintInfo对象。并在命令完成时删除此对象。
  
   CPrintInfo包含打印时的一般信息,例如:要打印页的范围,打印机的状态,当前正在打印的页
   这些信息存放在CPintInfo的对象中;此对象还包括在CPrint对话框中输入的值。
  
   在打印期间,一个CPrintInfo对象在框架和视图类之间传递,并且用于两者之间交换信息。例如:
   框架通过对CPrintInfo类的m_nCurPgae成员赋值,来通知视图类要打印文档的哪一页,视图类检索
   此值,并执行指定页的实际打印。
  
   另一个例子就是文档的长度到打印的时候也不知道多少页。视图类每打印一页都要检测是否到了文档的
   末尾。当到达文档的末尾时,视图类将CPrintInfo的m_bContinuePrinting成员设置为FALSE,通知框架
   停止打印循环。
   */
   /*
   Attach:把Windows设备上下文句柄附加在CDC对象上。
   CDC::Attach
   BOOL Attach(HDC hDC);
   返回值:如果成功,返回非零值,否则为0
   参数:
   hDC:Windows设备上下文。
   说明:
   使用这个函数把hDC附加到CDC对象上。
   */
   /*
   Detach:从CDC对象中分离出Windows设备上下文。
   CDC::Detach
   HDC Detach()
   返回值:Windows设备上下文句柄。
   说明:
   调用该函数将m_hDC从CDC对象中分离出来。并将m_hDC与m_bAttribDC设备为NULL。
   */
   void CPrintProj::Print()
   {
    CDC dc;
    CPrintDialog printDlg(FALSE);
       //利用CPrintDialog生成打印机设备环境
    if(printDlg.DoModual() == IDCANCEL) //让用户选择打印纸张等
       return;
    dc.Attach(printDlg.GetPrinterDC());//让Handle连接到dc上.
    dc.m_bPrinting = TRUE;

    CString strTitle;
    strTitle.LoadString(AFX_IDS_APP_TITLE);

    DOCINFO di; //DOCINFO中有相关的打印信息
    ::ZeroMemory(&di,sizeof(DOCINFO));
    di.cbSize = sizeof(DOCINFO);
    di.lpszDocName = strTitle; //设置标题

    BOOL bPrintingOK = dc.StartDoc(&di); //开始打印

    CPrintInfo Info;
    Info.m_rectDraw.SetRect(0,0,dc.GetDeviceCaps(HORZRES),dc.GetDeviceCaps(VERTRES)); //设置范围.

       OnBeginPrinting(&dc,&Info); //调用你自定义的打印功能.
    fo(UINT page = Info.GetMinPage();page < Info.GetMaxPage() && bPrintOK;page++)
    {
     Info.m_nCurPage = page;
     OnPrint(&dc,&Info); //调用你的"Print page"函数
     bPrintOK = dc.EndPage() > 0; //结束页
    }
    OnEndPrinting(&dc,&Info);//结束打印.

    if(bPrintingOK)
     dc.EndDoc();
    else
     dc.AbortDoc();
    dc.Detach();
   }
  
   说明:其实在Windows环境中是设备无关的.只要有了DC,就可以使用各种GDI函数,而不需要理会是在屏幕或是在
   打印机上绘图.

posted on 2010-04-21 20:45  scud001  阅读(2719)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3