最实用的GDI辅助类

来源:互联网 发布:JS确认框怎么设置 编辑:程序博客网 时间:2024/06/05 04:42

作者:SleepSheep

下载源代码
整体效果图:

有多少次你曾写下过类似的代码:

void Draw(CDC    *pDC){    CPen MyPen;    MyPen.CreatePen(PS_SOLID, 1, RGB(255, 0, 0));    CPen *pOldPen = pDC->SelectObject(&MyPen);    // 用pen画     // 对于brush、font等重复以上的动作     pDC->SelectObject(pOldPen);  // 重置之前的pen}      

如果你没有用MFC类来写,你可能会用以下相似的方法:

void Draw(HDC    hDC){    HPEN hMyPen = CreatePen(PS_SOLID, 1, RGB(255, 0,    0));    HPEN hOldPen = SelectObject(hDC, hMyPen);    //用pen来画     //对于brush、font等重复以上的动作     SelectObject(hDC, hOldPen);  // 重置之前的pen    DeleteObject(hMyPen);       // 销毁新建的pen}        

大部分的C/C++ Windows程序可能都充斥着这样的代码。那这样的代码有什么问题呢?主要的几个问题如下:

  1. 冗余
    你必须要写四行代码才能将一个简单的pen选入到DC中,而且对于font、bitmap、brush等也要重复同样的操作。
  2. 错误
    如果是直接用WIN32 API来写,你必须要记得,在函数返回前将旧的pen选入到DC中,并可能还需要将新的pen销毁掉。除此之外,如果有错误处理或多个return语句,你必须以某一种方式来确保函数中每条执行路径最终都会将原始的pen选回到DC中。在之前的例子中,假设注释的部分有一个return语句或发生了一个错误,你必须记得对以原始的pen为参数调用SelectObject,也许还需要调用DeleteObject
  3. 复杂
    你不得不将大量的参数传给函数用来创建pen、font、brush、bitmap等,尽管有些参数的默认值会在一定程度上对你有所帮助。(MFC库确实用了一些默认的参数,但像CPen::CreatePen()以及CFong::CreateFont()这样的成员中却并没有经常用)。

MFC代码如果能写成如下形式岂不是很好:

void Draw(CDC    *pDC){    CVCKSelectPen MyPen(pDC, RGB(255, 0, 0));  // 就一行     //用pen来画     //原始的pen会自动被选回DC中 }

或者非MFC的代码写成这种样子:

void Draw(HDC    hDC){    CVCKSelectPen MyPen(hDC, RGB(255, 0, 0));    //就一行     //用pen来画     //原始的pen会自动被选回DC中 }         

前面的代码有以下几个优点:

  1. 简洁
    一行代码就可以创建pen并将它选入到DC中,在你用完后会将它从DC中拿掉,在必要的时候会将pen销毁掉。
  2. 强健
    不管你如何退出程序,原始的pen会通过CVCKSelectPen的析构函数重新被选回到DC中,即使是有错误发生或有好几个return语句。
  3. 简单易用
    许多默认的参数已在pen的创建函数中传入了,许多成员函数(如创建brush需要的那些)都有重载,使得可以使用不一样的参数。

下面简单的就几个类及它们的使用方法来做一些介绍。

class CVCKSelect{     protected:     CDC * const    m_pDC;     CVCKSelect(CDC * pDC):m_pDC(pDC)    { ASSERT(m_pDC);}     virtual    ~CVCKSelect() {}};// Penclass CVCKSelectPen : public    CVCKSelect{    CPen * m_NewPen;    CPen * m_pOldPen;public:    CVCKSelectPen(CDC * pDC)        :CVCKSelect(pDC),m_pOldPen(NULL){}    CVCKSelectPen(CDC * pDC,    COLORREF col, int sty=PS_SOLID, int wid=0)        :CVCKSelect(pDC)    {        VERIFY(m_NewPen.CreatePen(sty,wid,col));        VERIFY(m_pOldPen=m_pDC->SelectObject(&m_NewPen));    }    CVCKSelectPen(CDC * pDC, CPen *    pPen)        :CVCKSelect(pDC)    {         ASSERT(pPen);        VERIFY(m_pOldPen=m_pDC->SelectObject(pPen));     }    ~CVCKSelectPen()     {         if    (m_pOldPen) VERIFY(m_pDC->SelectObject(m_pOldPen));     }    void    Select(CPen * pPen)    {         ASSERT(pPen);        ASSERT(pPen!=&m_NewPen);        CPen *    old=m_pDC->SelectObject(pPen); ASSERT(old);        if    (!m_pOldPen) m_pOldPen=old;        m_NewPen.DeleteObject();    }    void    Select(COLORREF col, int sty=PS_SOLID, int wid=0)    {         if    (m_pOldPen) Select(m_pOldPen);        VERIFY(m_NewPen.CreatePen(sty,wid,col));        VERIFY(m_pOldPen=m_pDC->SelectObject(&m_NewPen));    }    void    Restore()    {         if    (m_pOldPen) VERIFY(m_pDC->SelectObject(m_pOldPen));         m_pOldPen=NULL;        m_NewPen.DeleteObject();    }    CPen * Old() const { return m_pOldPen; }};// Brushclass CVCKSelectBrush    : public CVCKSelect{    CBrush * m_NewBrush;    CBrush * m_pOldBrush;public:     CVCKSelectBrush(CDC * pDC)        :CVCKSelect(pDC),m_pOldBrush(NULL)    {    }    // Solid brush    CVCKSelectBrush(CDC * pDC,    COLORREF crColor)         :CVCKSelect(pDC)    {         VERIFY(m_NewBrush.CreateSolidBrush(crColor));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    //    Hatch brush    CVCKSelectBrush(CDC * pDC, int index,COLORREF crColor)         :CVCKSelect(pDC)    {         VERIFY(m_NewBrush.CreateHatchBrush(index,crColor));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    //    Pattern brush    CVCKSelectBrush(CDC * pDC,    CBitmap * pBitmap)         :CVCKSelect(pDC)    {         ASSERT(pBitmap);        VERIFY(m_NewBrush.CreatePatternBrush(pBitmap));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    //    DIB Pattern brush    CVCKSelectBrush(CDC *    pDC,HGLOBAL hPackedDib,UINT usage)        :CVCKSelect(pDC)    {        VERIFY(m_NewBrush.CreateDIBPatternBrush(hPackedDib,usage));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    CVCKSelectBrush(CDC * pDC,    CBrush * pBrush)         :CVCKSelect(pDC)    {         ASSERT(pBrush);        VERIFY(m_pOldBrush=m_pDC->SelectObject(pBrush));     }    ~CVCKSelectBrush()    {        if    (m_pOldBrush) VERIFY(m_pDC->SelectObject(m_pOldBrush));     }    void    Select(CBrush * pBrush)     {         ASSERT(pBrush);        ASSERT(pBrush!=&m_NewBrush);        CBrush *    old=m_pDC->SelectObject(pBrush); ASSERT(old);        if    (!m_pOldBrush) m_pOldBrush=old;        m_NewBrush.DeleteObject();    }    //    Solid brush    void    Select(COLORREF col)     {         if    (m_pOldBrush) Select(m_pOldBrush);        VERIFY(m_NewBrush.CreateSolidBrush(col));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    //    Hatch brush    void    Select(int index,COLORREF col)     {         if    (m_pOldBrush) Select(m_pOldBrush);        VERIFY(m_NewBrush.CreateHatchBrush(index,col));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    //    Pattern brush    void    Select(CBitmap * pBitmap)     {         if    (m_pOldBrush) Select(m_pOldBrush);        VERIFY(m_NewBrush.CreatePatternBrush(pBitmap));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    //    DIB Pattern brush    void    Select(HGLOBAL hPackedDib,UINT usage)    {        if    (m_pOldBrush) Select(m_pOldBrush);        VERIFY(m_NewBrush.CreateDIBPatternBrush(hPackedDib,usage));        VERIFY(m_pOldBrush=m_pDC->SelectObject(&m_NewBrush));    }    void    Restore()    {         if    (m_pOldBrush) VERIFY(m_pDC->SelectObject(m_pOldBrush));         m_pOldBrush=NULL;        m_NewBrush.DeleteObject();    }    CBrush * Old() const { return m_pOldBrush; }};// Fontclass CVCKSelectFont  : public CVCKSelect{    CMyFont m_NewFont;    CFont *    m_pOldFont;public:     CVCKSelectFont(CDC * pDC)        :CVCKSelect(pDC),m_pOldFont(NULL)    {    }    CVCKSelectFont(CDC * pDC,int size,LPCTSTR face=NULL,BOOL bold=0,            BOOL italic=0, BOOL    underlined=0,BOOL fixed=0,            BOOL hiquality=0,int angleindegrees=0)         :CVCKSelect(pDC)    {         VERIFY(m_NewFont.MyCreateFont(m_pDC,size,face,bold,italic,                        underlined,fixed,hiquality,angleindegrees));        VERIFY(m_pOldFont=m_pDC->SelectObject(&m_NewFont));     }    CVCKSelectFont(CDC * pDC, CFont    * pFont)        :CVCKSelect(pDC)    {         ASSERT(pFont);        VERIFY(m_pOldFont=m_pDC->SelectObject(pFont));     }    ~CVCKSelectFont()    {         if    (m_pOldFont) VERIFY(m_pDC->SelectObject(m_pOldFont));     }    void Select(CFont * pFont)    {          ASSERT(pFont);         ASSERT(pFont!=&m_NewFont);         CFont *    old=m_pDC->SelectObject(pFont); ASSERT(old);         if    (!m_pOldFont) m_pOldFont=old;         m_NewFont.DeleteObject();    }    void    Select(int size,LPCTSTR face=NULL,BOOL    bold=0,                   BOOL italic=0,BOOL    underlined=0,BOOL fixed=0,                   BOOL hiquality=0,int angleindegrees=0)     {         if    (m_pOldFont) Select(m_pOldFont);        VERIFY(m_NewFont.MyCreateFont(m_pDC,size,face,bold,italic,                        underlined,fixed,hiquality,angleindegrees));        VERIFY(m_pOldFont=m_pDC->SelectObject(&m_NewFont));     }    void    Restore()    {        if    (m_pOldFont) VERIFY(m_pDC->SelectObject(m_pOldFont));         m_pOldFont=NULL;        m_NewFont.DeleteObject();    }    CFont * Old() const { return m_pOldFont; }}; 

CVCKSelect是这些类的基类,它里面存了一个CDC指针。
CVCKSelectPen、CVCKSelectBrush和CVCKSelectFont都派生自CVCKSelect,它们都对各自的构造函数和Select方法进行了重载。只要有合适的参数,一般只要构造函数那一步就可以将新的GDI对象建立出来并将它选入DC中,有时还可以再调用Select方法做一些修改。析构函数对会自动将原始的GDI对象选入DC中,并把新建的GDI对象销毁掉。Restore方法可以让使用者自己控制这一过程。Old方法会返回原始的GDI对象。
以上只展示了一部分GDI对象的封装,其他的请参考源代码。

以下是使用这些类的一个小例子:

void Draw(CDC * pDC) {    CVCKSelectPen         Pen(pDC,RGB(255,0,0));     CVCKSelectBrush       Brush(pDC,HS_BDIAGONAL,RGB(0,255,0));     CVCKSelectFont        Font(pDC,18,"Arial",TRUE);     CVCKSelectTextColor   TxtCol(pDC,RGB(0,0,255));     CVCKSelectTextAlign   TxtAlg(pDC,TA_CENTER|TA_BASELINE);     CVCKSelectBkMode      BkMode(pDC,TRANSPARENT);     // 绘制操作      // 新建的一些GDI对象会被自动释放,DC也会自动回复。  }  

以brush为例,有的时候要等到运行时才能确定到底该要创建什么样的,如下所示:

if (Condition1) {     // 选择 Hatch Brush } else {     // 选择 Solid Brush } // 使用brush 

这时,不能在某个条件下放一个CVCKSelectBrush对象,这样当出了if/else范围后那个对象的生命周期就结束了,DC中还是原来的brush。这个时候,我们要先在if/else之外用只接受一个CDC*为参数的构造函数建立一个CVCKSelectBrush对象,再在if/else中调用Select:

CSelBrush SelBrush(pDC); if (Condition1) {     SelBrush.Select(HS_BDIAGONAL,RGB(255,0,0)); } else {     SelBrush.Select(RGB(255,0,0)); } // 使用brush 

需要注意的是,在一个函数中,每一中GDI对象(pen、bursh……)或每一种DC的属性(text color、map mode……)都只能用一个对象来操作。
如果你不想要反复的使用这些类,而且你对性能上的一些牺牲可以忍受的话,那可以用CSaveDC。在创建时它会保存整个的DC,等它销毁的时候会将DC还原。

原创粉丝点击