关于如何换肤、子类化的解决方案
2010-06-23 20:40:47 来源:WEB开发网关于子类化及其撤销的顺序问题,当用自己的类或者过程子类化窗口时,需要处理好与MFC类子类化的顺序冲突。假设我们自己的类叫CWndNew,那么不管CWnd和CWndNew谁先子类化一个窗口,最终两者协同工作的结果应该是该窗口的窗口过程还原到未子类化之前的状态。首先,不要在HOOK过程中处理WM_NCDESTROY消息。理由:如果CWndNew比CWnd先子类化,由于HOOK的原因,你仍然会先处理WM_NCDESTROY,这时候如果你撤销子类化,那么CWnd类就得不到机会清理。而如果你不撤销子类化,CWnd没有能力把被子类化的窗口还原到最初状态。在HOOK过程中,不能通过调用SendMessage函数让CWnd先行处理,然后你自己再处理,因为SendMessage后,消息又会被HOOK拦截。
由于上述原因,在CWndNew的消息过程中处理WM_NCDESTROY是很不错的选择,MFC也是这样做的。参照如下的代码进行解释:
case WM_NCDESTROY:
{
LRESULT lret;
WNDPROC wndproc;
wndproc = (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC);
if (wndproc == CWndNew::StaticWindowProc)
{
HWND hWnd = m_hWnd;
UnsubclassWindow();
lret = CallWindowProc(m_oldProc,
hWnd,
uMsg,
wParam,
lParam);
}
else
{
lret = CallWindowProc(m_oldProc,
m_hWnd,
uMsg,
wParam,
lParam);
if(wndproc == (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC))
UnsubclassWindow();
}
delete this;
return lret;
}
首先判断该窗口的WNDPROC是否发生过变动,如果没有的话是最好的,赶紧撤销子类化,再把消息传递给之前窗口过程,然后功成身退,不问世事了。
如果发生过变动,那么也就是说有别的类在CWndNew子类化以后又进行了子类化,而现在又把WM_NCDESTROY传给了CWndNew。这好办,如法炮制,把消息继续往前传,如果WNDPROC又发生了改变,说明之前的某个窗口过程已经作了处理,就不需要再进行撤销子类化的操作了。这点MFC的CWnd类也是这样做的。
另外还有一个问题不解,就是Edit,ListBox,ListCtrl等等控件的内嵌的滚动条是怎么换肤的?网上一般介绍的方法是隐藏原来的,然后换上自己重新实现的。这种在Spy++中一看就能现出原形,可是Skin++ 换肤后的滚动条就不知道是怎么实现的了?我看过coolsb这个文章,他能实现给滚动条换肤的功能,但是对Combobox支持不好。
更多精彩
赞助商链接