双缓冲绘图和窗口控件的绘制——ATL ActiveX 窗口控件生成向导绘制代码OnDraw的一个错误 .
双缓冲绘图和窗口控件的绘制
---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
Qt编写自定义控件7-自定义可拖动多边形 自定义可拖动多边形控件,原创作者是赵彦博(QQ:408815041 zyb920@hotmail.com),创作之初主要是为了能够在视频区域内用户自定义可拖动的多个区域,即可用来作为警戒区域,也可用来其他的处理,拿到对应的多边形坐标集合,本控件的主要难点是如何计算一个点在一个多边形区域内,何时完成一个多边形区域,支持多个多边形。
WPF绘制自定义窗口 原文:WPF绘制自定义窗口 WPF是制作界面的一大利器,下面就用WPF模拟一下360的软件管理界面,360软件管理界面如下: 界面不难,主要有如下几个要素: 窗体的圆角 自定义标题栏及按钮 自定义状态栏 窗体的半透明效果 窗体4周有一圈半透明阴影(抓的图上看不出来) 实现思路很简单,首先隐藏默认窗口的标题栏和边框,然后用WPF的Border或Canvas等元素模拟定义窗体的标题栏、内容区和状态栏。
Qt 控件随窗口缩放 在Qt的界面设计中,我们有时候希望窗口在最大化的时候,上面的控件也跟着缩放,那么我们就需要调整控件的SizePolicy属性,关于这个属性的讲解请参见我之前的博客Qt SizePolicy 属性,由于窗口的拉伸,会导致控件的拉伸,有时候我们只需要某一个或者某几个控件被拉伸,那么我们可以将需要拉伸的控件的Horizontal Policy设为Minimum,表示可以放大不能缩小,然后将不希望拉伸的设为Fixed,设置好了之后,我们在空白处点击鼠标右键,选择Lay Out,选择其中的Lay Out in a Grid,如果有不合理的地方继续进行调整即可。
相关文章
- 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 6.全局防护Bypass之一些函数的错误使用
- js 时间戳转换,全网最牛业务逻辑解析,逐行解析代码,千万不要错过,里面有些错误场景你可能还不知道
- EF Core新增迁移时无法加载程序集“System.ValueTuple”的错误
- VS2010中asp.net调试.ashx程序错误的一个简单方法!
- java.lang.ClassNotFoundException: org.apache.poi.hssf.usermodel.HSSFWorkbook 错误的原因和解决方法
- javascript 语法规范错误提示代码
- 《MySQL排错指南》——1.3 当错误可能由之前的更新引起时
- Excel VLOOKUP实用教程之 07 vlookup如何解决肉眼看完全匹配,但是就是返回N/A错误(教程含数据excel)
- ubuntu22.04系统环境下使用vs code安装pylint检查python的代码错误
- mysql—mysql错误Every derived table must have its own alias解决
- Spring_错误 java.sql.SQLException: Lock wait timeout exceeded | CannotAcquireLockException 的解决
- 【Unity3D日常BUG】Unity3D中出现“unsafe code 不安全的代码”的错误时的解决方法
- MySQL错误:The user specified as a definer (XXX@XXX) does not exist
- validform校验框架不显示错误提示
- 记一次粗心大意的代码错误