自绘的RadioButton
转载自:小男人博客
前言:没想到这么久不来这里写东西了。其实真的是前段时间没什么东西好写的,毕竟肚子里面墨水不多。还有就是没有什么有价值的东西,只是觉得最近自己进步很慢,不过倒是不想以前那么散漫了,看到喜欢什么就学什么。看来我还是专心研究C++吧,呵呵。真是门很好很强大的语言。这里自己在做任务的时候接到了一个自绘RadioButton的控件,开始以为很简单,但是由于自己知识点的缺乏,以及对WINDOWS编程的生疏,导致一个很简单的问题拖了几乎一个礼拜才弄好,真的是非常郁闷的一件事情。不过正是印证了一句话,难者不会,会者不难啊。
自绘按钮控件其实是司空见惯的事情了,不过由于不知道其中的细节问题,并且说实话我在网上也没有搜到相关的解释资料(实在是我搜索能力的问题,这个问题的答案应该是很简单的),着实让我郁闷了很久。后来拿了别人的源码一看才恍然大悟,真是惭愧啊。惭愧的不是那技术我不懂,而是自己的懒惰,碰到拦路虎就绕开走了。
RadioButton的自绘只牵涉到两个步骤,一个绘制前面单选的位图,剩下的就是绘制后面的文字了。绘制其实不麻烦,只要设置了BS_OWNERDRAW样式,并且在DrawItem内部得到CDC后按位置贴图和输出文字就可以了。关键的就是要去响应设置和按下的消息,并且要让Windows通过API去得到对应的标记位,做到跟系统提供的按钮一样。所以只是在鼠标按下和释放的时候绘制不同的位图上去是不够的,还需要设置其标记位(我没有自己去求证过,但是通过程序的编写和运行,可以得知,当设置了BS_OWNERDRAW样式后,CButton::SetCheck(int)和CButton::GetCheck()的调用是不能起到作用的,必须自己设置,但是内部的结构是如何,我无从知晓,如果能知道内部保存Check标记的变量的话,那就又好做了,只要直接设置其值就可以了,不过CButton::SetCheck(int)和CButton::GetCheck()的源码是可以看的,大家看一下就明白它是怎么做的了)。
流程和关键点已经说明了,现在就来看下接下来会用到的一些知识点:
1.自绘按钮:
要自绘按钮,首先要设置当前控件为BS_OWNERDRAW风格,这个可以在控件的PreSubclassWindow函数里面完成。比如RadioButton在属性里面是没有BS_OWNERDRAW样式可以选择的,那么就只能通过手动添加代码来达到这一目的:CWnd::ModifyStyle(0, BS_OWNERDRAW。然后重写DrawItem函数,从函数的LPDRAWITEMSTRUCT lpDrawItemStruct参数上得到CDC的句柄,并且通过CDC::FromHandle来转化为一个CDC指针。)
POINT pt = point;
::ClientToScreen(m_hWnd, &pt);
HWND hWndMouseOver = ::WindowFromPoint(pt);
// 当鼠标在当前控件上,并且按键按下标记为True时
if(hWndMouseOver == m_hWnd && m_bPressed)
{
…
}
m_bPressed = FALSE;
|
typedefstructtagDRAWITEMSTRUCT{
UINT CtlType; // 控件类型
UINT CtlID;// 控件的ID号
UNIT itemID;//菜单项的索引
UINT itemAction;// 绘图操作
UINT itemState; // 状态
HWND hwndItem; // 控件的窗口句柄
HDC hDC; // 相关的设备环境
RECT rcItem;//控件的范围
DWORD itemData;// 指定与菜单项相联系的应用程序定义的32位值
} DRAWITEMSTRUCT;
|
_AFXWIN_INLINE void CButton::SetCheck(int nCheck)
{ ASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, BM_SETCHECK, nCheck, 0); }
_AFXWIN_INLINE int CButton::GetCheck() const
{ ASSERT(::IsWindow(m_hWnd)); return (int)::SendMessage(m_hWnd, BM_GETCHECK, 0, 0); }
|
COleControlContainer* m_pCtrlCont; // for containing OLE controls
…
UINT CWnd::IsDlgButtonChecked(int nIDButton) const
{
ASSERT(::IsWindow(m_hWnd));
if (m_pCtrlCont == NULL)
return ::IsDlgButtonChecked(m_hWnd, nIDButton);
else
return m_pCtrlCont->IsDlgButtonChecked(nIDButton);
}
|
{
RADIOSTATE_SELECT, // 选中
RADIOSTATE_UNSELECT, // 未选中
RADIOSTATE_SELMOUSEHOVER, // 选中状态鼠标滑过
RADIOSTATE_UNSELMOUSEHOVER, // 未选中状态鼠标滑过
RADIOSTATE_PRESS, // 选中状态按下
RADIOSTATE_DISABLE // 不可用(灰色)
} RadioState;
关于高亮的解释就是当鼠标滑过按钮时,有一个表示可以捕获焦点的提示性显示。具体在位图上表现为一圈阴影。当前位图的存储格式为单行存储,一行6个位图为一套。绘制的时候,用这个枚举变量的值作为位图状态的偏移量来裁剪需要的状态位图到设备环境中。
大体的说明介绍到这里,下面提供具体的源码,压缩包内提供响应的使用说明。觉得好用的话大家可以随意使用,但是请不要删除最上方的说明信息,谢谢。如果能提出您宝贵的意见就更好了,欢迎交流。