下面分析一下自绘按钮的原理,用过mfc自绘按钮的人都知道,是通过重载了父窗口wm_drawitem的响应消息实现的。同时也要子类化按钮来得到按钮的其他有用的消息,比如wm_mousemove、wm_keydown等消息。因为mfc的消息循环都是封装好的,所以只要派生一下基本控件类就可以了。当是用win32api做的话就需要自己来子类化按钮窗口的消息循环了,相信经常编程的朋友都知道,子类化控件要用到setwindowlong来改变窗口的回调过程,然后在回调窗口内添上自己需要处理的消息即可。因为我们要实现自绘按钮所以最好把子类化的过程做成一个类,然后传给它要自绘的按钮句柄就行了。因为要在类里面实现消息回调函数,但是类里面的消息回调函数只能是静态的,所以不能对应每个实例的消息回调。在我实现的按钮子类化类里,我用到thunk技术或setprop函数来实现的,具体请网上查找。
下面我来谈谈自绘按钮里最重要的部分,就是响应按钮消息函数里的wm_paint消息,我们所有的自绘动作都在这里进行的。wm_paint里的绘图操作与普通窗口的操作一样,但是为了跟踪按钮的当前状态,我们还要响应按钮窗口的wm_mousemove、wm_setfocus、wm_killfocus、wm_lbuttondown、wm_enable等消息来得到当前按钮的状态。从而在wm_paint里面绘出不同的状态,能实现的东西很多可以说你想多少基本就能实现多少^_^,看个人喜好了,我提供源代码大家可以自行修改。我也是参看了buttonst里面自绘的代码,我自己添加了右键拖动功能,鼠标掠过发生功能大家有兴趣可以自己添加,锻炼一下自己的编程能力^_^。
下面我说一下我做的这个类的一个问题,我把按钮类做成了一个动态库,调用时只要加上我的头文件和连接的lib库就可以了。我的动态库在win32的程序加载是没有问题的,但是在mfc里面,必需要响应父窗口的wm_drawitem消息,在里面直接返回,而不要调用mfc默认的处理就ok了。这是因为我没有截获父窗口的wm_drawitem消息,否则在关闭程序时会出现非法操作!主要代码分析如下:
自绘按钮类声明:
class dllport cwinbutton
{
public:
//初始化按钮(这是第一步!)
bool getitemhwnd(hwnd hwnd);
//还原按钮区域设置
bool restore();
//设置按钮是否可以拖动
bool setdrag(bool enable);
//设置按钮图标
bool seticon(hicon icon);
//设置按钮文字
bool settext(char *text, hfont font);
bool settext(char *text);
bool settext(char *text, colorref color);
//设置按钮有效区域
bool setupregion(colorref transcolor);
lresult onpaint(hdc hdc);
//设置按钮无效时的图片
bool setdisablepic(hbitmap bmp);
//设置按钮按下时的图片
bool setpresspic(hbitmap bmp);
//设置悬停按钮时的图片
bool sethoverpic(hbitmap bmp);
//设置按钮背景图片,第二个参数是是否根据图片调整按钮大小
bool setbackpic(hbitmap bmp, bool bresize);
//设置按钮的提示消息
bool settooltip(char *text);
cwinbutton();
virtual ~cwinbutton();
private:
static lresult winapi stdproc(hwnd hwnd,uint umsg,uint wparam,long lparam);
wndproc getthunk();
wndproc createthunk();
lresult callback winproc(uint message, wparam wparam, lparam lparam);
bool drawinsideborder(hdc dc, rect *rect);
bool drawflat(hdc dc, rect *rect);
bool drawdefault(hdc dc);
hwnd m_tooltip;
hwnd m_hwnd;
hwnd m_hwndparent;
long m_oldproc;
wndproc m_thunk;
toolinfo ti;
hicon m_icon;
hbitmap m_back; //按钮背景图片
hbitmap m_hove; //鼠标悬停时按钮背景图片
hbitmap m_press; //鼠标按下时按钮背景图片
hbitmap m_disable; //按钮无效时背景图片
bitmap bm;
colorref m_textcolor; //按钮文字的颜色
bool m_bmousetracking; //判断鼠标是否在窗口内
bool m_bpress; //判断鼠标是否按下
bool m_enable; //控件是否有效
bool m_bfocus; //按钮是否处于输入焦点
bool m_bownerdraw; //判断是否用户自己贴图
bool m_bdrag; //是否处于拖动状态
bool m_bdragenable; //是否允许拖动
char m_text[max_textlen]; //按钮文字
char m_tiptext[max_textlen]; //按钮提示文字
hfont m_font; //按钮文字字体
hcursor m_oldcursor;
rect m_parentrt;
rect m_beginrt;
rect m_currentrt;
point m_beginpt;
point m_currentpt;
int m_captionheight;
int m_borderwidth;
int m_edgewidth;
protected:
//按钮的外边框
hpen m_boundrypen;
//鼠标指针置于按钮之上时按钮的内边框
hpen m_insideboundrypenleft;
hpen m_insideboundrypenright;
hpen m_insideboundrypentop;
hpen m_insideboundrypenbottom;
//按钮获得焦点时按钮的内边框
hpen m_insideboundrypenleftsel;
hpen m_insideboundrypenrightsel;
hpen m_insideboundrypentopsel;
hpen m_insideboundrypenbottomsel;
//按钮的底色,包括有效和无效两种状态
hbrush m_fillactive;
hbrush m_fillinactive;
};
消息回调类里的实现代码:
cwinbutton::getitemhwnd()里面
if(setprop(m_hwnd, "cwinbutton", (handle)this) == 0)
{
outputdebugstring("setprop error");
return false;
}
m_oldproc = setwindowlong(m_hwnd,gwl_wndproc,(long)stdproc);
cwinbutton::stdproc()里面
{
cwinbutton* w = (cwinbutton*)getprop(hwnd, "cwinbutton");
return w->winproc(umsg,wparam,lparam);
}