在MFC中为Static控件贴图

本文讲述的在静态文本控件贴图的方法,及其中可能发生的问题,虽然这不是什么特别难的事情,但有时总是那么的想当然,结果就出现了问题,本人当初也是这样的,所以觉得有必要把本文写下来,以供他人参考,假如你看到我这篇文章,并且因此解决了问题,请感谢你自己,因为是你搜索能力强悍,因为本人的博客实在是没什么有含金量的东西,搜到实属不易。

首先建立一个基于对话框的工程
然后,为CXXXDialog类中,private标号下添加如下代码,CStatic *m_pStatic(注:本例采用动态添加控件的方式);添加在OnInitDialog中添加如下代码
m_pStatic=new CStatic;
m_pStatic->Create(“”,WS_VISIBLE|WS_BORDER|WS_CHILD|SS_BITMAP|SS_CENTERIMAGE,CRect(20,20,50,50),this);为了说明我想说的问题,在这里文本框的位置选择了一个比较接近于对话框中间的值。
接下来,在OnPaint函数中添加以下代码,即可完成在文本控件上贴图。
CXXXDialog::OnPaint()
{

CRect m_ClientSize;
m_pStatic->GetClientRect(&m_ClientSize);//获取控件客户区的大小
HBITMAP hBitmap;
m_Bitmap.LoadBitmap(IDB_BITMAP1);//将位图读取到m_Bitmap中
hBitmap=(HBITMAP)m_Bitmap.m_hObject;
CDC * pDC=m_pStatic->GetDC();//定义一个控件的CDC
CDC memdc;
memdc.CreateCompatibleDC(pDC);//创建兼容DC
memdc.SelectObject(hBitmap);
BITMAP bmp;
GetObject(hBitmap,sizeof(bmp),&bmp);//获取位图的相关信息
int x = bmp.bmWidth;//位图的宽度
int y = bmp.bmHeight;//位图的高度
pDC->StretchBlt(m_ClientSize.top,m_ClientSize.left,m_ClientSize.right,m_ClientSize.bottom,&memdc,0,0,x,y,SRCCOPY);//将位图贴到控件上
UpdateWindow();
}
接着,咱们来看另外一段代码
CXXXDialog::OnPaint()
{

CRect m_ClientSize;
m_pStatic->GetWindowRect(&m_ClientSize);//注意对比此处与上面代码的差异
HBITMAP hBitmap;
m_Bitmap.LoadBitmap(IDB_BITMAP1);//将位图读取到m_Bitmap中
hBitmap=(HBITMAP)m_Bitmap.m_hObject;
CDC * pDC=m_pStatic->GetDC();//定义一个控件的CDC
CDC memdc;
memdc.CreateCompatibleDC(pDC);//创建兼容DC
memdc.SelectObject(hBitmap);
BITMAP bmp;
GetObject(hBitmap,sizeof(bmp),&bmp);//获取位图的相关信息
int x = bmp.bmWidth;//位图的宽度
int y = bmp.bmHeight;//位图的高度
pDC->StretchBlt(m_ClientSize.top,m_ClientSize.left,m_ClientSize.right,m_ClientSize.bottom,&memdc,0,0,x,y,SRCCOPY);//将位图贴到控件上
UpdateWindow();
}
编译运行之后呢,你会发现,位图并没有如你所愿贴上去。
然后,我们可以来调试一下,对比两种情况,前者m_ClientSize的top,left,right,bottom四个成员的值分别是0,0,28,28,而后者m_ClientSize的top,left,right,bottom四个成员的值分别是316,608,346,638,再看看我们的贴图函数,pDC->StretchBlt(m_ClientSize.top,m_ClientSize.left,m_ClientSize.right,m_ClientSize.bottom,&memdc,0,0,x,y,SRCCOPY);该函数是pDC的成员,而pDC是属于m_pStatic的,也就是说我们在贴图的时候选择的坐标应该是m_pStatic的客户区坐标,而非其他,而很明显,后者所得到的坐标是Static控件在屏幕上的坐标,这也难怪未能贴图成功。本人当初是在控件充满对话的情况下贴图的,当初有点歪打正着,我直接调用GetClientRect这个函数,很明显,在OnPaint函数中直接调用得到是对话框的客户区坐标,而控件充满对话框情况下的客户区的坐标跟对话框的客户区坐标比较接近(有一两个像素的差异,但是贴图之后肉眼是看不出来的,但是控件的边框会被覆盖掉),所以刚好能贴图成功。然后后来在另外一个程序中,有点类似本文的情况,控件非充满,结果就老是出问题。经过一番折腾,才知道问题的所在。

MFC基础,MFC自绘控件学习总结.

前言:从这学期开始就一直在学习自绘控件(mfc),目标是做出一款播放器界面,主要是为了打好基础,因为我基础实在是很烂….说说我自己心得体会以及自绘控件的方法吧,算是吐槽吧,说的不对和不全的地方,或者有更好的方法,请不吝赐教。

我的机器环境是:Windows7旗舰版 Service Pack 1,Visual studio 2005

1).重绘某个控件时,强烈推荐使用子类化方法,比如想自绘Button控件, 首先添加自己的类CMYButton 继承自 CButton ,声明一个CMYButton 对象,然后使用 SubclassDlgItem(UINT nID, CWnd* pParent ); // 第一个参数表示控件ID,第二个参数表示指向父窗口对象指针,一般用this表示(如果不想用SubclassDlgItem。那么可以使用CMYButton自身提供的Create方法 动态创建一个Button),这样子就可以在自己类中添加重写WindowProc()这个窗口过程函数了,非常,非常,重要 ,其他控件自绘都参考这一条.

2).我入手的第一个控件是 Button,我终于知道我的基础有多烂,很多基本的函数如GetDlgItem() , SubclassDlgItem() 都不知道,查资料,看源码 ,费了不少时间才基本完成Button的自绘,另外自绘

的按钮默认情况下是不能响应键盘按下Enter的,需要额外做一些处理。(关键词:BS_OWNERDRAW ,DrawItem),(在后期我仿造qq登陆的Button加了个效果,Hover和Leave时是渐变的,只在设置

对话框里面的Button使用了)

3).然后是 RadioButton ,CheckBox 其实和Button异曲同工的,推荐了解3个API函数CheckRadioButton(),SetCheck(),GetCheck().

4).另外对于不规则按钮实现需要掌握 SetWindowRgn(),CombineRgn(),SelectClipRgn() 3个API函数,其他不规则窗口,控件也可以参考这个方法。

5).然后是Edit控件自绘,不算是完全自绘,只重绘了非客户区(如果没特殊需要也没必要重绘客户区),和改变背景颜色,改变字体,不过后期我加了个效果,鼠标在Edit上和离开Edit时边框是渐变的(关

键词:CtlColor,WM_NCPAINT),RichEdit也可以用这个方法

6).然后是ToolTip(气泡提示控件),微软提供了NM_CUSTOMDRAW这个通告消息,以WM_NOTIFY形式发送,可以用MFC类向导添加到自己的派生类中,不过我推荐重写OnPaint函数,完全自绘(难点:

需要根据文本内容计算出控件的大小,显示位置等)
在后期我实现了,淡入,点击/超时 淡出的效果(需要映射TTN_POP 和TTN_SHOW两个通告消息),不过挪开淡出效果没能实现,求指导。

7).然后是Sliderctrl, 微软提供了NM_CUSTOMDRAW这个通告消息,以WM_NOTIFY形式发送,可以用MFC类向导添加到自己的派生类中,前期我是用的这种方法,不过后期发现这种方法局限性很大,

推荐重写OnPaint函数,完全自绘(关键点:在PreSubclassWindow 里面把 Thumb(拇指按钮),Channel(凹槽),以及整个控件大小保存起来,以便在OnPaint里面绘制)

8).然后是Staic控件,这个比较简单,重写OnPaint函数 画上文本,把DC设为透明模式就行了,有人会说直接在CtlColor直接SetBkMode(TRANSPARENT)就行了,不用在OnPaint处理,但这是有个问

题的, 如果要求文本一直变化,旧的文本没有擦除,新设置的文本又盖上了。所以根据这个控件的用途,自己选择适合的方法吧。

9).然后是Menu,这个较难,严格来说Menu 并不算控件,他是派生自CObject类的,微软提供了MeasureItem,DrawItem两个虚函数类供自绘 ,MeasureItem作用是计算出菜单的高度和宽度,系统

会自动根据文本内容最长那项来作为Menu的宽度。DrawItem作用顾名思义就是画了,但是有个致命的问题,自绘出来的Menu 有个系统默认的边框,十分邪恶和难看,(ModifyStyle和

SetWindowLong去不掉边界的)这时到自己派生的CMYMenu里面 发现微软只给咱们提供了仅仅5个虚函数,没有提供WindowProc()这个窗口过程函数,这不是坑爹嘛…….这时一般做法都是派生自

CWnd 自己实现菜单的功能.不过查了下资料任然可以自绘的:需要使用钩子 替换菜单的窗口过程,在WM_CREATE时 去掉边界Stytle 有兴趣的朋友可以Google一下。(难点:替换菜单窗口过程)

10). 然后是Combobox控件,这个较难, 微软提供了CompareItem,DeleteItem,DrawItem,MeasureItem 4个虚函数供自绘。我只用了后2个,(如果只加了 CBS_SORT 必须重写CompareItem这个

函数,除非使用了CBS_HASSTRINGS | CBS_SORT就可以不重写CompareItem()), 别以为这样子就完了,运行后,打开Combobox 显示的 List 有系统默认的边框!!!ModifyStyle和SetWindowLong

去不掉边界.老规矩查资料去,不看不知道,一看吓一跳,Combobox 是由3个控件组合成成的(难怪叫组合框),分别是Edit,Listbox,和combo本身(除去Edit ,Listbox剩下那部分),当时我就震惊了,迷茫

了! 这时需要添加OnCtlColor这个函数,在里面 使用SubclassWindow()这个API函数子类化 ListBox 和 Edit(在这之前 你还需要准备自绘好的 ListBox控件 和Edit控件)Combobox 有3种样式

CBS_SIMPLE, CBS_DROPDOWNLIST,CBS_DROPDOWN,第一种不说了不常用,第二种是不能输入只能点击选择,第三种可以输入可点击选择. 我的程序里面使用的是CBS_DROPDOWNLIST样式

11).Combobox 在 Windows7 下疑惑:关闭滑动打开组合框特效 ,自绘Combobox是可以去掉边界并进行窗口剪切的(圆角矩形) , 如果是 开启滑动打开组合框特效,系统会加上边框, 剪切的圆角又

变成直角了, 跟踪调试发现是在WM_WINDOWPOSCHANGING 消息里面搞的鬼 ,有兴趣的朋友可以对比看看,暂时为找到解决方案。。。
开启/关闭 滑动打开组合框特效 :计算机-右键属性-高级系统设置-高级-性能设置-视觉效果

12).然后是 TabCtrl ,微软提供了DrawItem,MeasureItem 2 个虚函数供自绘,需要加 TCS_OWNERDRAWFIXED这个Stytle,表明这个控件需要自绘,不过我没有用这种方式,我直接重写了OnPaint函数

完全自绘(难点:需要自己计算每个标签大小,位置,以及与之绑定的Dialog显示位置)

13).最后是窗体框架绘制(非客户区),这个较难,看了很多例子源码,也花了不少时间,WM_MOVE,WM_PAINT ,WM_NCPAINT,WM_NCACTIVATE,这4个消息自绘成功的关键 , 在绘制时候还需要

计算出边框/标题栏的大小和位置(Win7 和Xp 下 GetSystemMetrics()返回值是不同的)。
// 给出框架绘制不闪烁的关键代码 ,完全原创。
if(message == WM_NCACTIVATE && !wParam) // wParam=0, deactive
{
return 1; // 必须返回1,处理默认消息(如果不返回1,一切弹出的窗口(模态,非模态)不能点击)
}
if(message == WM_NCACTIVATE && wParam) // wParam =1, active
{
return 0; // 这个随便返回(0和1都行)
}
if(message==WM_NCPAINT)
{
return 0; // 阻止默认框架绘制(An application returns zero if it processes this message 摘自MSDN)
}
这种方式是保留了边界和标题栏,其实就是盖住了原来的画上自己的,只要不闪烁就是成功的。当然也可以去掉系统默认的边界 和 标题栏,在客户区算出一个边界和标题栏,处理一些消息,能实现更好

的框架自绘,做出更漂亮的界面。

14)可能是我这个人比较蛋疼吧,想着既然做了个播放器界面为什么不给他实现一个播放的功能呢,微软提供了 MCI—媒体控制接口,自己封装了一个播放类,实现了一些播放基本功能。这样子第一个

版本就算完成了吧。

—测试环境: 6台Win7 和 2台Xp
—界面测试:Win7 和 Xp 运行均正常(有个缺陷见第11条)
—播放测试: 我的电脑上可以 播放rmvb,RM ,AVI,MP4,WMV,FLV ,部分测试Win7电脑6个格式全部能播放,但部分测试的Win7电脑只能播放 AVI 和MP4,WMV格式(为什么?求解答…).
—另外WMV格式增加播放速度和减少播放速度,貌似是不行的。
—在XP系统 下能打开视频文件但只能听见声音看不见图像,求解答?
这结果确实比较蛋疼,大家可以测试下看看界面是否正常,播放功能是否正常

15)鉴于MCI版本播放测试不是很令人满意,我又再一次蛋疼了,因为一个前辈说让我用Activex 控件试试。好吧,花了2天时间,研究了下Activex 控件, 用OleView研究了Aplayer这个控件,
APlayer_001.dll 这个DLL文件就是Aplayer Activex控件(Activex 控件使用前必须先注册,如果只是在代码里面注册了APlayer_001.dll,程序可以运行但是播放不了文件,因为在播放文件的时候

Aplayer控件 还会根据播放文件类型 再加载 一些DLL文件和AX文件,那些文件加起来有80多M,坑爹啊这是….)其实Aplaer 是迅雷看看播放器的一个组件, 如果安装了迅雷的话,在 C:\

\Program Files\\Common Files\\Thunder Network 可以找到 Aplaer 这个文件夹,如果没有安装迅雷或者没有Aplayer这个组件程序是不能运行的。
备注:这个APlayer本身有个缺陷,在播放 Rmvb和mkv文件时,点击定位不准确。对于大的文件,很难发现这个缺陷,可以找个 一两分钟的短视频文件用迅雷看看打开 点击定位试试,缺陷非常明显。
由于Aplayer 流程 和MCI不太一样,所以花了点时间做了第二个版本。

—测试环境:6台Win7 和 2台Xp
—界面测试:Win7 和 Xp 运行均正常(有个缺陷见第11条)
—播放测试:Win7上可以播放RMVB,RM, AVI,MP4,WMV,FLV ,MKV, MP3, WMA, WAV
—在XP系统下 打开文件任然只能听见声音看不见图像,对于XP系统这个播放问题,还未找到解决方案,求指导(难道是因为我注册的是Win7的APlayer?)。
—另外程序有个缺陷 :在第一次 点击Aplayer控件的时候会缩小,调试发现根本没有进入 WM_LBUTTONDOWN,直接 WM_WINDOWPOSCHANGING,WM_WINDOWPOSCHANGED,WM_SIZE
,不知道这个消息从哪里发过来的….(我用了个不是很好的方法解决了,既然是在第一次点击的时候才会缩小,我在PreSubclassWindow里面 :PostMessage(WM_LBUTTONDOWN,MK_LBUTTON,

(LPARAM)&UserDown);PostMessage(WM_LBUTTONUP,MK_LBUTTON,(LPARAM)&UserDown);)这2个消息,貌似没再出现了缩小情况了,但昨天我运行的时候又出现这个问题了,出现的概率很低….

无语啊)

16)
程序的测试全部由自己完成,很多功能没来得及测试,所以程序可能会出现这样那样的问题,一个人力不从心啊,希望拍砖温柔点啊。
程序热键,做的不好,不是全局和后台的,必须窗口获得焦点才能响应.
在后期增加了托盘功能。
增加了播放列表功能,最多支持10个文件,大于10个覆盖第10个,双击列表中的文件名,就可以播放这个文件。不过播放列表我没有单独做个窗口是放在设置对话框第3个标签的。
应同学的强烈要求增加了拖拽打开文件功能(我过滤了一些文件扩展名,不是每个文件类型拖拽都有效,mci版本和Activex版本支持的格式不同 ,过滤情况也不一样)
程序最初叫IKAN Player 但是发现 PPLive 已经用了这个名字, 改成了ICAN Player….

附加说明:我现在大3在读,现在正在实习找工作阶段,上次面试的时候把程序给面试官演示的时候,他说了句“这种程序网上随便一搜一大把”…..他这个想法我很能理解,如果我是面试官,我也会持怀疑态度的. 所以现在不方便给出源码,希望大家理解我,以后一定会给出项目源码的.

帖子不知道怎么上传附件,程序放在CSDN 资源区内,不需要资源分的,大家下下来看下吧。
给出链接: http://download.csdn.net/source/3428958

论坛上也有我的帖子:http://topic.csdn.net/u/20110710/19/5209f358-31c8-4057-b108-02155a417fd0.html?61362

大家运行下,看看有没有什么问题和异常啊,欢迎回帖

本是后山人,偶坐前堂客;醉舞经阁半卷书,坐井说天阔。 大志戏功名,海斗量福祸。论到囊中羞涩时,怒指乾坤错。