baidu

[Win32]什么是控件

[Win32]什么是控件
声明:
这个题目起的非常的大,写完了,有一点后悔了.毕竟我只写了不到两个月的Win32程序,对其认识还不是很全面.如有错误,请路过的各位神仙达人指出来,也算是对小弟的帮助.:-)

 

一.    Windows的消息机制
    Windows是消息驱动的(看"现代操作系统",里面管这个这种叫事件驱动),发生消息,响应消息,本身就是一个松散耦合的设计.
    不管是什么窗口,Create之前都需要注册一下类别,都需要RegisterClass.这个Class的概念,和OO里面的Class的概念没有本质的区别,而CreateWindow需要制定ClassName,所创建出来的也就是这个Class的一个实例.
    而对消息的响应,是在Class的成员WndProc里面完成.为啥放到这里?龙生龙,凤生凤,老鼠的孩子会打洞~~同一个Class对消息处理的形式是一样的,因为他是同一个Class.
    再来看这个松散耦合结构的原理.
    由于CreateWindow时需要指出ClassName,那么一个窗口便会有一个ClassName,一个WndProc,这在外面来是比较直观而且比较重要的.一个HWND会有一个WndProc,这是显而易见的.有了这点,主线程就不用操心了,他只需要从消息队列里面拿出消息,分发下去,然后执行.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    这就是我们常见的消息循环.
    因为MSG有一个成员HWND,而一个HWND有成员ClassName,进而能得知WndProc,那么DispatchMessage执行也就变得比较容易.
    主线程只需要一个劲的GetMessage,DispatchMessage就行,拿到谁的消息,就到谁的WndProc哪里去执行.
    

    static LRESULT ImageButtonProc(HWND,UNIT,WPARAM,LPARAM);
    WNDPROC procImageButton = NULL;
    WNDPROC procOld = NULL;
    
    procImageButton = ImageButtonProc;
    procOld = (WNDPROC)GetWindowLong(hWnd,GWL_WNDPROC);
    BOOL result = SetWindowLong(hWnd,GWL_WNDPROC,(LONG)procImageButton);
    ASSERT(result);

    static LRESULT ImageButtonProc(HWND hWnd,UNIT message,WPARAM wParam,LPARAM lParam)
    {
        LRESULT result = 0;
        switch(message)
        {
        case WM_ERASEBKGND:
            return TRUE;
        case WM_PAINT:
            //在这里画
            return result;
        case WM_LBUTTONDOWN:
            SetCapture(hWnd);
            return result;
        case WM_LBUTTONUP:
            ReleaseCapture();
            //这里就发生了Click事件
            //当然这只是假设,还需要根据鼠标点击位置具体判断
            return result;
        }
        result = CallWindowProc(procOld,hWnd,message,wParam,lParam);
    }

    以上是我们写独立的控件的前键.
    另外一个条件就是我们需要对窗口过程进行控制.因为Button点下去是要相应OnClick的,而Lable点击下去,基本上什么都不做.正是因为这种差异性,导致我们需要对WndProc进行控制.
Windows本身就考虑到这一点,所以他提供了RegisterClass,允许我们注册自己的Class,对WndProc进行一些个性化的处理.而Windows默认的控件,比如Static,比如Edit,对消息的处理,都是他
设定好的.
    有了RegisterClass,那么我们就可以写很多控件,只需我们搞1个或者n个WndProc就可以.
    OK,那是一种办法,可是我不想用那种方法.
    Windows还为我们提供了另外一种机制.我们要做一个个性化的控件,无非就是要一个窗口句柄和他的消息处理么,只要我们能拿到这些就完事.
    GetWindowLong/SetWindowLong就能帮我们做些事.
    OK,来看怎么做.

二.    自己打造一个控件
    其实这里,我之前都说过了,翻翻我的Blog就能找到.
    不管你用什么办法或得到的窗口句柄hWnd,要想把它做成一个ImageButton,那么基本上只需要这来:
   
    如果我们把上面的ImageButtonProc填写完整,那么,那个窗口hWnd就变成了一个ImageButton,不管他之前是ListView,还是Static.可以看到GetWindowLong/SetWindowLong给了我们很大的想象空间.

三.    考虑封装
    在上面,已经完成了一个控件,这个这个控件还不是很容易使用,其根本原因是编程模型的问题.那玩意儿是纯过程的模型,跟我们平时见到的OO模型的类库感觉上不太一样.虽然我们也可以通过某些方式,使得那个东西好使起来,用着也挺顺手,那就需要用"一"的一些思想,以及依赖倒置的C实现,否则貌似还真有一点麻烦.本文暂时还不想讨论这个问题....
    各个控件对于消息的处理是不同的,而只有控件自己才知道自己需要怎么的处理,这有可能还是一个动态的不同.而窗口过程显然是全局的,是静态的,他对消息的处理只能是静态.所以要想搞成类库的话,必须要解决这个问题!
    这让我想起了MFC,MFC那种OO封装的方式,你可以在自己的类里面处理各种各样的消息,而不去管真正的消息循环.
    现在还是要看看"一",Windows本身给我提供了很多想法,需要我们好好去想.DispatchMessage拿到消息,只是简单的根据hWnd,找到他的窗口过程,执行了起来.而,hWnd->WndProc,这本身就是一个映射的概念.他通过这个映射,解决了依赖问题.
    我们也可以这么做,只是这个映射也是需要我们自己搞定的!

//这个只是大概的原型
//说明原理而已    
class Control
{
public:
    HWND hWnd;
    Control()
    {
        //xxxxx
        //这里就是上面的,想办法搞一个hWnd,然后替换窗口过程
    }
    virtual LRESULT WndProc(HWND,UINT,WPARAM,LPARAM);
    static void Register(HWND,Control*);
protected:
    WNDPROC newProc;
    WNDPROC oldProc;
};

//Register的实现
static map<HWND,Control*> controlCache;
void Control::Register(HWND hWnd,Control* pControl)
{
    ASSERT(hWnd && pControl);
    controlCache.insert(make_pair<HWND,Control*>(hWnd,pControl));
}


LRESULT DefaultWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    map<HWND,Control*>::iterator i = controlCache.find(hWnd);
    ASSERT(i!=controlCache.end());
    if(i!=controlCache.end())
    {
        return (*i)->WndProc(hWnd,message,wParam,lParam);
    }
    return DefWindowProc(hWnd,message,wParam,lParam);
}


代码的原型就是那个样子,默认的窗口过程只是分发消息,真正的处理在你自己的控件里面.
想法来源于一两个星期前用Win32做UI的时候,虽然最后放弃了OO封装(因为时间不够),但是我觉得简单的OO封装,可以方便构造出我们想要的控件.
因为Windows默认控件都比较丑陋,美化的话基本上只有画,而画的话,我用什么画不是画,为什么非要基于Win32默认控件提供的那种方式来自绘呢?我曾经使用过ListView,发觉那个控件不是ListView,是上帝控件,超级复杂,后来我放弃了那个控件,自己用Static画了一个,代码也不过200行,而且定制性还很强.(一个GridView也是类似的)

PS:
1. 有人管GetWindowLong/SetWindowLong叫子类化,想想也是,我那处理也本身就是继承的过程.
2. 后来跟群里面一个朋友聊,发现我所认识的其实很早就有了,MFC就是类似的,底层只负责分发,上层处理
3. 说白了,我对OO的认识是比较片面的,所以我才能写出来别人几十年之前实现的东西%>_<%
4. 因为我没看过模板(因为TC++PL里面没写...),二者公司不允许使用WTL,所以我基本上不了解ATL/WTL那一套机制,不知道他是怎么处理和分发消息的
   这要是本文的一个缺陷,认识比较片面    

 

posted @ 2010-05-26 09:07  egmkang  阅读(2905)  评论(13编辑  收藏  举报