zoukankan      html  css  js  c++  java
  • [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那一套机制,不知道他是怎么处理和分发消息的
       这要是本文的一个缺陷,认识比较片面    

  • 相关阅读:
    C语言I博客作业08
    第十一周助教总结
    C语言I博客作业07
    C语言I博客作业06
    C语言I博客作业05
    C语言I作业004
    第十三周助教总结
    C语言I博客作业09
    第十二周助教总结
    C语言I博客作业08
  • 原文地址:https://www.cnblogs.com/egmkang/p/1744106.html
Copyright © 2011-2022 走看看