双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .

简介: 双缓冲绘图和窗口控件的绘制 ---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误   cheungmine 我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后希望在这个窗口中绘制我们的图像、图形等数据,然而ATL向导生成的代码中包含很多...

双缓冲绘图和窗口控件的绘制

---ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误

 

cheungmine


我们通常使用ATL COM组件,生成一个带窗口的ActiveX控件,然后希望在这个窗口中绘制我们的图像、图形等数据,然而ATL向导生成的代码中包含很多错误,下面是其自动向导生成的代码:

    HRESULT OnDraw(ATL_DRAWINFO& di)
    {
        RECT& rc = *(RECT*)di.prcBounds;
        // 将剪辑区域设置为 di.prcBounds 指定的矩形
        HRGN hRgnOld = NULL;
        if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
            hRgnOld = NULL;
        bool bSelectOldRgn = false;

        HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);

        if (hRgnNew != NULL)
        {
            bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
        }

        Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
        SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
        LPCTSTR pszText = _T("ATL 8.0 : Canvas");
#ifndef _WIN32_WCE
        TextOut(di.hdcDraw,
            (rc.left + rc.right) / 2,
            (rc.top + rc.bottom) / 2,
            pszText,
            lstrlen(pszText));
#else
        ExtTextOut(di.hdcDraw,
            (rc.left + rc.right) / 2,
            (rc.top + rc.bottom) / 2,
            ETO_OPAQUE,
            NULL,
            pszText,
            ATL::lstrlen(pszText),
            NULL);
#endif

        if (bSelectOldRgn)
            SelectClipRgn(di.hdcDraw, hRgnOld);

        return S_OK;
    }


请注意这里面包含一个错误,改正之后的代码(红色字体):

    HRESULT OnDraw(ATL_DRAWINFO& di)
    {
        RECT& rc = *(RECT*)di.prcBounds;
        // 将剪辑区域设置为 di.prcBounds 指定的矩形
        HRGN hRgnOld = NULL;
        if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
            hRgnOld = NULL;
        bool bSelectOldRgn = false;

        HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);

        if (hRgnNew != NULL)
        {
            bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
        }

        Rectangle(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
        SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
        LPCTSTR pszText = _T("ATL 8.0 : Canvas");
#ifndef _WIN32_WCE
        TextOut(di.hdcDraw,
            (rc.left + rc.right) / 2,
            (rc.top + rc.bottom) / 2,
            pszText,
            lstrlen(pszText));
#else
        ExtTextOut(di.hdcDraw,
            (rc.left + rc.right) / 2,
            (rc.top + rc.bottom) / 2,
            ETO_OPAQUE,
            NULL,
            pszText,
            ATL::lstrlen(pszText),
            NULL);
#endif


        if (bSelectOldRgn)
            SelectClipRgn(di.hdcDraw, hRgnOld);

 

        // 删除剪辑区域
        ::DeleteObject(hRgnNew);  // Add by cheungmine. MUST!!

 

        return S_OK;
    }

 

注意其中绿色的代码,你应该完全注释掉这种绘制的逻辑,而采用双缓冲。因此,ATL自动生成的OnDraw代码是不适合实际的绘图控件的。下面的代码是我更改之后的,增加了双缓冲机制:

    void MyDrawCode (HDC hdc, RECT &rc)

    {

Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
        SetTextAlign(di.hdcDraw, TA_CENTER|TA_BASELINE);
        LPCTSTR pszText = _T("ATL 8.0 : Canvas");
#ifndef _WIN32_WCE
        TextOut(hdc,
            (rc.left + rc.right) / 2,
            (rc.top + rc.bottom) / 2,
            pszText,
            lstrlen(pszText));
#else
        ExtTextOut(hdc,
            (rc.left + rc.right) / 2,
            (rc.top + rc.bottom) / 2,
            ETO_OPAQUE,
            NULL,
            pszText,
            ATL::lstrlen(pszText),
            NULL);
#endif

    }


    void DbBufferDraw(HDC hdcDraw, RECT &rcClip)
    {
        HDC         hMemDC  = ::CreateCompatibleDC(hdcDraw);             
        ATLASSERT(hMemDC);       
        HBITMAP  hBmpNew = ::CreateCompatibleBitmap(hdcDraw, WidthRect(rcClip), HeightRect(rcClip));           
        ATLASSERT(hBmpNew);
        HBITMAP  hBmpOld = (HBITMAP) ::SelectObject(hMemDC, hBmpNew);

 

          // 添加自己的绘制代码

        MyDrawCode(hMemDC, rcClip);

        if (IsWindow()) {
            ::BitBlt ( hdcDraw,
                     rcClip.left,
                     rcClip.top,
                     WidthRect(rcClip), HeightRect(rcClip),
                     hMemDC, 
                     rcClip.left,
                     rcClip.top,
                   SRCCOPY );
        }
        else {
            ::BitBlt ( hdcDraw,
                     rcClip.left + m_rcPos.left,
                     rcClip.top  + m_rcPos.top,
                     WidthRect(rcClip), HeightRect(rcClip),
                     hMemDC, 
                     rcClip.left,
                     rcClip.top,
                   SRCCOPY );
        }

        // 释放 hMemDC
        ::SelectObject(hMemDC, hBmpOld);
        ::DeleteObject(hBmpNew);
        ::DeleteDC(hMemDC);
    }


    HRESULT OnDraw(ATL_DRAWINFO& di)
    {
        RECT& rc = *(RECT*)di.prcBounds;
        // 将剪辑区域设置为 di.prcBounds 指定的矩形
        HRGN hRgnOld = NULL;
        if (GetClipRgn(di.hdcDraw, hRgnOld) != 1)
            hRgnOld = NULL;
        bool bSelectOldRgn = false;

        HRGN hRgnNew = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);

        if (hRgnNew != NULL)
        {
            bSelectOldRgn = (SelectClipRgn(di.hdcDraw, hRgnNew) != ERROR);
        }

        // 双缓冲
     
DbBufferDraw(di.hdcDraw, rc);


        if (bSelectOldRgn)
            SelectClipRgn(di.hdcDraw, hRgnOld);

        // 删除剪辑区域
        ::DeleteObject(hRgnNew);  // Add by cheungmine. MUST!!

        return S_OK;
    }

 

按上面的修改,烦人的闪烁没了。另外,在OnDarw中,没必要把全部绘制代码放入 MyDrawCode 中。因为 MyDrawCode 如果执行时间较长,则 OnDraw会显得很慢。因此,光是双缓冲还不够,因为OnDraw被调用的时候,都是系统激发的,我们只需要把原来保存的绘制图片直接绘制到hMemDC中即可,也就是, MyDrawCode中不可以如本例所示的那样,放置实际绘制的代码,而是只把图片重新拷贝到hdc上即可,如:

    void MyDrawCode (HDC hdc, RECT &rc)

    {

        m_BkgndMap.CopyTo(hdc, rc);

    }

 

    m_BkgndMap 可以是自己实现的Image或CImage等图像类。

 

因此,在一个基本的绘图系统中,至少需要3个缓冲层次:


第一层:控件窗口HDC(无窗口控件也是存在HDC的)

第二层:控件窗口HDC的兼容MemDC,即:HDC hMemDC  = ::CreateCompatibleDC(hdcDraw);

第三层:后台图片HDC包装类: m_BkgndMap

 

关于如何创建这样的ActiveX 窗口控,请看我的相关文章:

http://blog.csdn.net/cheungmine/archive/2007/10/10/1818913.aspx

 

 

from:http://blog.csdn.net/ubuntu64fan/article/details/5917979

目录
相关文章
|
API C# Windows
Winform控件优化之无边框窗体及其拖动、调整大小和实现最大最小化关闭功能的自定义标题栏效果
Winform中实现无边框窗体只需要设置FormBorderStyle = FormBorderStyle.None,但是无边框下我们需要保留移动窗体、拖拽调整大小、自定义美观好看的标题栏等...
2958 0
Winform控件优化之无边框窗体及其拖动、调整大小和实现最大最小化关闭功能的自定义标题栏效果
|
算法 Windows
Winform控件优化之实现无锯齿的圆角窗体(或任意图形的无锯齿丝滑的窗体或控件)【借助LayeredWindow】
在一般能搜到的所有实现圆角窗体的示例中,都有着惨不忍睹的锯齿...而借助于Layered Windows,是可以实现丝滑无锯齿效果的Form窗体的,其具体原理就是分层窗体....
1205 0
Winform控件优化之实现无锯齿的圆角窗体(或任意图形的无锯齿丝滑的窗体或控件)【借助LayeredWindow】
C#(三十五)之在滚动窗口中绘图
窗体中的三个属性: Size:窗体大小(包括标题栏和边框) ClientSize:工作区大小(不包括标题栏和边框) AutoScrollMinSize:出现滚动条的最小尺寸
196 0
C#(三十五)之在滚动窗口中绘图
QT软件开发: 点击鼠标在窗口里绘制矩形(窗口透明背景)
QT软件开发: 点击鼠标在窗口里绘制矩形(窗口透明背景)
494 0
QT软件开发: 点击鼠标在窗口里绘制矩形(窗口透明背景)
PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
373 0
PyQt5 技术篇-调用颜色对话框(QColorDialog)获取颜色,调色板的调用。
|
C# 前端开发 JavaScript
WPF绘制自定义窗口
原文:WPF绘制自定义窗口 WPF是制作界面的一大利器,下面就用WPF模拟一下360的软件管理界面,360软件管理界面如下:   界面不难,主要有如下几个要素: 窗体的圆角 自定义标题栏及按钮 自定义状态栏 窗体的半透明效果 窗体4周有一圈半透明阴影(抓的图上看不出来) 实现思路很简单,首先隐藏默认窗口的标题栏和边框,然后用WPF的Border或Canvas等元素模拟定义窗体的标题栏、内容区和状态栏。
1492 0
|
C#
WPF如何为程序添加splashScreen(初始屏幕)
原文:WPF如何为程序添加splashScreen(初始屏幕) 一、考虑到大部分的splashscreen其实都只是一个图片,所以最简单的做法是,先导入一张图片,然后设置它的生成操作为“splash screen”   二、通过程序设置SplashScreen public parti...
1365 0
Qt 控件随窗口缩放
在Qt的界面设计中,我们有时候希望窗口在最大化的时候,上面的控件也跟着缩放,那么我们就需要调整控件的SizePolicy属性,关于这个属性的讲解请参见我之前的博客Qt SizePolicy 属性,由于窗口的拉伸,会导致控件的拉伸,有时候我们只需要某一个或者某几个控件被拉伸,那么我们可以将需要拉伸的控件的Horizontal Policy设为Minimum,表示可以放大不能缩小,然后将不希望拉伸的设为Fixed,设置好了之后,我们在空白处点击鼠标右键,选择Lay Out,选择其中的Lay Out in a Grid,如果有不合理的地方继续进行调整即可。
3545 0