⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mfc

📁 很好的MFC书籍
💻
📖 第 1 页 / 共 4 页
字号:
<P align=justify>(1)使用设备描述表的步骤</P>
<P align=justify>要使用设备描述表,一般有如下步骤:</P>
<UL>
  <P align=justify>
  <LI>获取或者创建设备描述表; 
  <P></P>
  <P align=justify></P>
  <LI>必要的话,改变设备描述表的属性; 
  <P></P>
  <P align=justify></P>
  <LI>使用设备描述表完成绘制操作; 
  <P></P>
  <P align=justify></P>
  <LI>释放或删除设备描述表。 
  <P></P></LI></UL>
<P 
align=justify>Common设备描述表通过::GetDC,::GetDCEx,::BeginPaint来获得一个设备描述表,用毕,用::ReleaseDC或::EndPaint释放设备描述表;</P>
<P align=justify>Printer设备描述表通过::CreateDC创建设备描述表,用::DeleteDC删除设备描述表。</P>
<P align=justify>Memory设备描述表通过::CreateCompatibleDC创建设备描述表,用::DeleteDC删除。</P>
<P align=justify>Information设备描述表通过::CreateIC创建设备描述表,用::DeleteDC删除。</P>
<P align=justify>(2)改变设备描述表属性的途径</P>
<P align=justify>要改变设备描述表的属性,可通过以下途径:</P>
<P align=justify>用::SelectObject选入新的除调色板以外的GDI Object到设备描述表中;</P>
<P 
align=justify>对于调色板,使用::SelectPalette函数选入逻辑调色板,并使用::RealizePalette把逻辑调色板的入口映射到物理调色板中。</P>
<P align=justify>用其他API函数改变其他属性,如::SetMapMode改变映射模式。</P>
<OL>
  <OL>
    <OL>
      <P align=justify>
      <LI><A name=_Toc445888987></A><A name=_Toc445782390></A><A 
      name=_Toc452640886></A><A name=_Toc457298951></A><B>设备描述表在MFC中的实现</B> 
      <P></P></LI></OL></OL></OL>
<P align=justify>MFC提供了CDC类作为设备描述表类的基类,它封装了Windows的HDC设备描述表对象和相关函数。</P>
<OL>
  <P align=justify>
  <LI>CDC类 
  <P></P>
  <P align=justify>CDC类包含了各种类型的Windows设备描述表的全部功能,封装了所有的Win32 GDI 
  函数和设备描述表相关的SDK函数。在MFC下,使用CDC的成员函数来完成所有的窗口绘制工作。</P>
  <P align=justify>CDC 类的结构示意图2-2所示。</P><IMG height=146 hspace=12 
  src="MFC教程_ MFC和Win32.files/image107.gif" width=360 align=left> 
  <P align=justify></P>
  <P 
  align=justify>CDC类有两个成员变量:m_hDC,m_hAttribDC,它们都是Windows设备描述表句柄。CDC的成员函数作输出操作时,使用m_Hdc;要获取设备描述表的属性时,使用m_hAttribDC。</P>
  <P 
  align=justify>在创建一个CDC类实例时,缺省的m_hDC等于m_hAttribDC。如果需要的话,程序员可以分别指定它们。例如,MFC框架实现CMetaFileDC类时,就是如此:CMetaFileDC从物理设备上读取设备信息,输出则送到元文件(metafile)上,所以m_hDC和m_hAttribDC是不同的,各司其责。还有一个类似的例子:打印预览的实现,一个代表打印机模拟输出,一个代表屏幕显示。</P>
  <P align=justify>CDC封装::SelectObject(HDC hdc,HGDIOBJECT 
  hgdiobject)函数时,采用了重载技术,即它针对不同的GDI对象,提供了名同而参数不同的成员函数:</P>
  <P align=justify>SelectObject(CPen *pen)用于选入笔;</P>
  <P align=justify>SelectObject(CBitmap* pBitmap)用于选入位图;</P>
  <P align=justify>SelectObject(CRgn *pRgn)用于选入剪裁区域;</P>
  <P align=justify>SelectObject(CBrush *pBrush)用于选入刷子;</P>
  <P align=justify>SelectObject(CFont *pFont)用于选入字体;</P>
  <P align=justify>至于调色板,使用SelectPalette(CPalette *pPalette,BOOL 
  bForceBackground )选入调色板到设备描述表,使用RealizePalletter()实现逻辑调色板到物理调色板的映射。</P>
  <P align=justify></P>
  <LI>从CDC派生出功能更具体的设备描述表 
  <P></P></LI></OL>
<P align=justify>从CDC 派生出四个功能更具体的设备描述表类。层次如图2-3所示。</P>
<P align=justify></P>
<P align=justify><IMG height=166 src="MFC教程_ MFC和Win32.files/image108.gif" 
width=396></P>
<P align=justify></P>
<P align=justify>下面,分别讨论派生出的四种设备描述表。</P>
<UL>
  <P align=justify>
  <LI>CCientDC 
  <P></P></LI></UL>
<P align=justify>代表窗口客户区的设备描述表。其构造函数CClientDC(CWnd 
*pWin)通过::GetDC获取指定窗口的客户区的设备描述表HDC,并且使用成员函数Attach把它和CClientDC对象捆绑在一起;其析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用::ReleaseDC释放设备描述表HDC。</P>
<UL>
  <P align=justify>
  <LI>CPaintDC 
  <P></P></LI></UL>
<P 
align=justify>仅仅用于响应WM_PAINT消息时绘制窗口,因为它的构造函数调用了::BeginPaint获取设备描述表HDC,并且使用成员函数Attach把它和CPaintDC对象捆绑在一起;析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用::EndPaint释放设备描述表HDC,而::BeginPaint和::EndPaint仅仅在响应WM_PAINT时使用。</P>
<UL>
  <P align=justify>
  <LI>CMetaFileDC 
  <P></P></LI></UL>
<P align=justify>用于生成元文件。</P>
<UL>
  <P align=justify>
  <LI>CWindowDC 
  <P></P></LI></UL>
<P align=justify>代表整个窗口区(包括非客户区)的设备描述表。其构造函数CWindowDC(CWnd 
*pWin)通过::GetWindowDC获取指定窗口的客户区的设备描述表HDC,并使用Attach把它和CWindowDC对象捆绑在一起;其析构函数使用Detach把设备描述表HDC分离出来,调用::ReleaseDC释放设备描述表HDC。</P>
<OL>
  <OL>
    <OL>
      <P align=justify>
      <LI><A name=_Toc445888988></A><A name=_Toc445782391></A><A 
      name=_Toc452640887></A><A name=_Toc457298952></A><B>MFC设备描述表类的使用</B> 
      <P></P></LI></OL></OL></OL>
<OL>
  <P align=justify>
  <LI>使用CPaintDC、CClientDC、CWindowDC的方法 
  <P></P>
  <P align=justify>首先,定义一个这些类的实例变量,通常在栈中定义。然后,使用它。</P>
  <P align=justify>例如,MFC中CView对WM_PAINT消息的实现方法如下:</P>
  <P align=justify>void CView::OnPaint()</P>
  <P align=justify>{</P>
  <P align=justify>// standard paint routine</P>
  <P align=justify>CPaintDC dc(this);</P>
  <P align=justify>OnPrepareDC(&amp;dc);</P>
  <P align=justify>OnDraw(&amp;dc);</P>
  <P align=justify>}</P>
  <P 
  align=justify>在栈中定义了CPaintDC类型的变量dc,随着构造函数的调用获取了设备描述表;设备描述表使用完毕,超出其有效范围就被自动地清除,随着析构函数的调用,其获取的设备描述表被释放。</P>
  <P align=justify>如果希望在堆中创建,例如</P>
  <P align=justify>CPaintDC *pDC;</P>
  <P align=justify>pDC = new CPaintDC(this)</P>
  <P align=justify>则在使用完毕时,用delete删除pDC:</P>
  <P align=justify>delete pDC;</P>
  <P align=justify></P>
  <P align=justify></P>
  <LI>直接使用CDC 
  <P></P></LI></OL>
<P 
align=justify>需要注意的是:在生成CDC对象的时候,并不像它的派生类那样,在构造函数里获取相应的Windows设备描述表。最好不要使用::GetDC等函数来获取一个设备描述表,而是创建一个设备描述表。其构造函数如下:</P>
<P align=justify>CDC::CDC()</P>
<P align=justify>{</P>
<DIR>
<P align=justify>m_hDC = NULL;</P>
<P align=justify>m_hAttribDC = NULL;</P>
<P align=justify>m_bPrinting = FALSE;</P></DIR>
<P align=justify>}</P>
<P align=justify>其析构函数如下:</P>
<P align=justify>CDC::~CDC()</P>
<P align=justify>{</P>
<DIR>
<P align=justify>if (m_hDC != NULL)</P>
<P align=justify>::DeleteDC(Detach());</P></DIR>
<P align=justify>}</P>
<P 
align=justify>在CDC析构函数中,如果设备描述表句柄不空,则调用DeleteDC删除它。这是直接使用CDC时最好创建Windows设备描述表的理由。如果设备描述表不是创建的,则应该在析构函数被调用前分离出设备描述表句柄并用::RealeaseDC释放它,释放后m_hDC为空,则在析构函数调用时不会执行::DeleteDC。当然,不用担心CDC的派生类的析构函数调用CDC的析构函数,因为CDC::~CDC()不是虚拟析构函数。</P>
<P align=justify>直接使用CDC的例子是内存设备上下文,例如:</P>
<P align=justify>CDC dcMem; //声明一个CDC对象</P>
<P align=justify>dcMem.CreateCompatibleDC(&amp;dc); //创建设备描述表</P>
<P align=justify>pbmOld = dcMem.SelectObject(&amp;m_bmBall);//更改设备描述表属性</P>
<P align=justify>…//作一些绘制操作</P>
<P align=justify></P>
<P align=justify>dcMem.SelectObject(pbmOld);//恢复设备描述表的属性</P>
<P align=justify>dcMem.DeleteDC(); //可以不调用,而让析构函数去删除设备描述表</P>
<OL>
  <OL>
    <P align=justify>
    <LI><A name=_Toc445888989></A><A name=_Toc445782392></A><A 
    name=_Toc452640888></A><A name=_Toc457298953></A><B>GDI对象</B> 
    <P></P></LI></OL></OL>
<P align=justify>在讨论设备描述表时,已经多次涉及到GDI对象。这里,需强调一下:GDI对象要选入Windows 
设备描述表后才能使用;用毕,要恢复设备描述表的原GDI对象,并删除该GDI对象。</P>
<P align=justify>一般按如下步骤使用GDI对象:</P>
<P align=justify>Create or get a GDI OBJECT hNewGdi;</P>
<P align=justify></P>
<P align=justify>hOldGdi = ::SelectObject(hdc, hNewGdi)</P>
<P align=justify>……</P>
<P align=justify>::SelectObject(hdc, hOldGdi)</P>
<P align=justify>::DeleteObject(hNewGdi)</P>
<P 
align=justify>先创建或得到一个GDI对象,然后把它选入设备描述表并保存它原来的GDI对象;用毕恢复设备描述表原来的GDI对象并删除新创建的GDI对象。</P>
<P align=justify>需要指出的是,如果hNewGdi是一个Stock GDI对象,可以不删除(删除也可以)。通过</P>
<DIR>
<DIR>
<P>HGDIOBJ GetStockObject(</P></DIR>
<P>int fnObject // type of stock object </P>
<P align=justify>);</P></DIR>
<P align=justify>来获取Stock GDI对象。</P>
<P align=justify></P>
<OL>
  <P align=justify>
  <LI>MFC GDI对象 
  <P></P>
  <P align=justify>MFC用一些类封装了Windows GDI对象和相关函数,层次结构如图2-4所示:</P>
  <P align=justify></P><IMG height=176 hspace=12 
  src="MFC教程_ MFC和Win32.files/image109.gif" width=373 align=left> 
  <P align=justify></P>
  <P align=justify>CGdiObject封装了Windows GDI 
  Object共有的特性。其派生类在继承的基础上,主要封装了各类GDI的创建函数以及和具体GDI对象相关的操作。</P>
  <P align=justify></P><IMG height=125 hspace=12 
  src="MFC教程_ MFC和Win32.files/image110.gif" width=372 align=left> 
  <P 
  align=justify>CGdiObject的构造函数仅仅让m_hObject为空。如果m_hObject不空,其析构函数将删除对应的Windows 
  GDI对象。MFC GDI对象和Windows GDI对象的关系如图2-5所示。</P>
  <P align=justify></P>
  <LI>使用MFC GDI类的使用 
  <P></P></LI></OL>
<P align=justify>首先创建GDI对象,可分一步或两步创建。一步创建就是构造MFC对象和Windows 
GDI对象一步完成;两步创建则先构造MFC对象,接着创建Windows 
GDI对象。然后,把新创建的GDI对象选进设备描述表,取代原GDI对象并保存。最后,恢复原GDI对象。例如:</P>
<P align=justify>void CMyView::OnDraw(CDC *pDC)</P>
<P align=justify>{</P>
<P align=justify>CPen penBlack; //构造MFC CPen对象</P>
<P align=justify>if (penBlack.CreatePen(PS_SOLID, RGB(0, 0, 0)))</P>
<P align=justify>{</P>
<DIR>
<P align=justify>CPen *pOldPen = pDC-&gt;SelectObject(&amp;penBlack)); 
//选进设备表,保存原笔</P>
<P align=justify>…</P>
<P align=justify>pDC-&gt;SelectObject(pOldPen); //恢复原笔</P></DIR>
<P align=justify>}else</P>
<P align=justify>{</P>
<DIR>
<P align=justify>…</P></DIR>
<P align=justify>}</P>
<P align=justify>}</P>
<P 
align=justify>和在SDK下有一点不同的是:这里没有DeleteObject。因为执行完OnDraw后,栈中的penBlack被销毁,它的析构函数被调用,导致DeleteObject的调用。</P>
<P align=justify>还有一点要说明:</P>
<P align=justify>pDC-&gt;SelectObject(&amp;penBlack)返回了一个CPen 
*指针,也就是说,它根据原来PEN的句柄创建了一个MFC 
CPen对象。这个对象是否需要删除呢?不必要,因为它是一个临时对象,MFC框架会自动地删除它。当然,在本函数执行完毕把控制权返回给主消息循环之前,该对象是有效的。</P>
<P align=justify>关于临时对象及MFC处理它们的内部机制,将在后续章节详细讨论。</P>
<P 
align=justify>至此,Windows编程的核心概念:窗口、GDI界面(设备描述表、GDI对象等)已经陈述清楚,特别揭示了MFC对这些概念的封装机制,并简明讲述了与这些Windows 
Object对应的MFC类的使用方法。还有其他Windows概念,可以参见SDK开发文档。在MFC的实现上,基本上仅仅是对和这些概念相关的Win32函数的封装。如果明白了MFC的窗口、GDI界面的封装机制,其他就不难了。</P>
<P>
<SCRIPT language=JavaScript>write_tail();</SCRIPT>
</P>
<HR>

<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center border=0>
  <TBODY>
  <TR>
    <TD align=middle><A href="http://www.vczx.com/tutorial/mfc/mfc1.php" 
      target=_self>上一章</A> <A href="http://www.vczx.com/tutorial/mfc/mfc.php" 
      target=_self>回目录</A> <A href="http://www.vczx.com/tutorial/mfc/mfc3.php" 
      target=_self>下一章</A></TD></TR></TBODY></TABLE>
<P>&nbsp;</P>
<P>&nbsp;</P></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -