最实用的GDI辅助类

作者: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() {}
};

// Pen
class 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; }
};

// Brush
class 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; }
};

// Font
class 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还原。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值