zoukankan      html  css  js  c++  java
  • WTL窗口超类化(superclassing),窗口子类化(subclassing)

    超类化根据已有的(windows系统中已经注册过的)窗口类,比如“Edit”,”Button”等,复制其WNDCLASS(EX)结构,构造一个新类,并提供额外的功能和行为。

    例如,如果需要编写一个只接受数字输入的Edit控件(当用户输入非数字符号时控件发出警告声),可超类化windows内置的Edit控件,新控件的窗口类名为”Number_Only_Edit”,(可以在Spy++工具中看到此窗口类名称):

    class CNumberOnlyEdit :
    
        public CWindowImpl<CNumberOnlyEdit>
    
    {
    
    public:
    
        //Superclassing the "Edit" control in Windows
    
        DECLARE_WND_SUPERCLASS(_T("Number_Only_Edit"),_T("Edit"))
    
        BEGIN_MSG_MAP_EX(CNumberOnlyEdit)
    
            MSG_WM_CHAR(OnChar)
    
        END_MSG_MAP()
    
        
    
        void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    
        {
    
            //Only number or backspace is allowed
    
            if (('0'<=nChar && nChar<='9') || (nChar==VK_BACK))
    
            {
    
                //Accept the default process
    
                SetMsgHandled(FALSE);
    
            }
    
            else
    
            {
    
                ::MessageBeep(MB_ICONERROR);    //Warning
    
            }
    
        }
    
    };

    在窗口或对话框中使用此自定义的控件:

    //#define IDC_EDIT_NUMBER_ONLY 123
    
    CRect rc(10,10,300,30);
    
    m_NumberOnlyEdit.Create(
    
        m_hWnd,
    
        rc,
    
        _T("NumberOnlyEdit Superclassing"),
    
        WS_CHILD|WS_VISIBLE,
    
        NULL,
    
        IDC_EDIT_NUMBER_ONLY);

    值得注意的是,在定义CNumberOnlyEdit类时,所指定的基类是CWindowImpl<CNumberOnlyEdit>,因此m_NumberOnlyEdit实例只能使用CWindow类的方法,而不能使用CEdit类的函数(操作Edit控件的函数)。如果在代码中要用到CEdit类的函数,可将基类改为:

    class CNumberOnlyEdit :
    
        public CWindowImpl<CNumberOnlyEdit,CEdit>
    
    {
    
    //......
    
    }


     

    这样,就可以使用CEdit类的成员函数操作Edit控件了,例如:

    //Get the count of line in Edit box
    
    int lineCount = m_NumberOnlyEdit.GetLineCount();

    二.窗口子类化(Subclassing)

    窗口子类化可以改变一个已有的窗口(通常是对话框控件)的行为,以只接受数字输入的Edit控件为例,在WTL中主要有两种方法实现:

    1.如果要将特殊消息处理代码放到单独的模块中,可以自定义一个类:

    class CNumberOnlyEdit :
    
        public CWindowImpl<CNumberOnlyEdit,CEdit>
    
    {
    
    public:
    
        BEGIN_MSG_MAP_EX(CNumberOnlyEdit)
    
            MSG_WM_CHAR(OnChar)
    
        END_MSG_MAP()
    
        
    
        void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    
        {
    
            //Only number or backspace is allowed
    
            if (('0'<=nChar && nChar<='9') || (nChar==VK_BACK))
    
            {
    
                //Accept the default process
    
                SetMsgHandled(FALSE);   //or DefWindowProc();
    
            }
    
            else
    
            {
    
                ::MessageBeep(MB_ICONERROR);    //Warning
    
            }
    
        }
    
    };

    然后与已有的控件关联:

    //CNumberOnlyEdit m_NumberOnlyEdit;
    
    m_NumberOnlyEdit.SubclassWindow(GetDlgItem(IDC_EDIT_NUMBER));
    
    m_NumberOnlyEdit.SetWindowText(_T("NumberOnlyEdit Subclassing"));
    
    int lineCount = m_NumberOnlyEdit.GetLineCount();
    
    //...

    如果使用WTL的DDX功能,则可以很方便的将控件变量与控件ID关联:

    class CMainDlg : 
    
        public CDialogImpl<CMainDlg>,
    
        public CWinDataExchange<CMainDlg>
    
    {
    
    private:
    
        CNumberOnlyEdit m_NumberOnlyEdit;
    
    public:
    
        enum { IDD = IDD_MAINDLG };
    
        BEGIN_MSG_MAP_EX(CMainDlg)
    
            MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    
            COMMAND_ID_HANDLER(IDOK, OnOK)
    
            ALT_MSG_MAP(12)
    
                MSG_WM_CHAR(OnChar)
    
        END_MSG_MAP()
    
        BEGIN_DDX_MAP(CMainDlg)
    
            DDX_CONTROL(IDC_EDIT_NUMBER,m_NumberOnlyEdit)
    
        END_DDX_MAP()
    
    public:
    
        CMainDlg()
    
        {
    
        }
    
    public:
    
        LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        {
    
            // center the dialog on the screen
    
            CenterWindow();
    
            // set icons
    
            HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
    
            SetIcon(hIcon, TRUE);
    
            HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
    
            SetIcon(hIconSmall, FALSE);
    
            DoDataExchange(FALSE);
    
            m_NumberOnlyEdit.SetWindowText(_T("NumberOnlyEdit Subclassing"));
    
            int lineCount = m_NumberOnlyEdit.GetLineCount();
    
            return TRUE;
    
        }
    
        void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    
        {
    
            //Only number or backspace is allowed
    
            if (('0'<=nChar && nChar<='9') || (nChar==VK_BACK))
    
            {
    
                //Accept the default process
    
                SetMsgHandled(FALSE);   //or DefWindowProc();
    
            }
    
            else
    
            {
    
                ::MessageBeep(MB_ICONERROR);    //Warning
    
            }
    
        }
    
        //...
    
    };

    DDX在内部调用SubclassWindow()来将控件变量和控件ID进行关联。

    2.不必自定义个一个单独的类,而是将特殊消息的处理代码放到父窗口(对话框)的消息映射中:

    class CMainDlg : 
    
        public CDialogImpl<CMainDlg>
    
    {
    
    private:
    
        CContainedWindowT<CEdit> m_NumberOnlyEdit;
    
    public:
    
        enum { IDD = IDD_MAINDLG };
    
        BEGIN_MSG_MAP_EX(CMainDlg)
    
            MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
    
            COMMAND_ID_HANDLER(IDOK, OnOK)
    
            ALT_MSG_MAP(12)
    
                MSG_WM_CHAR(OnChar)
    
        END_MSG_MAP()
    
    public:
    
        CMainDlg()
    
            : m_NumberOnlyEdit(this,12)
    
        {
    
            //...
    
        }
    
    public:
    
        LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
    
        {
    
            // center the dialog on the screen
    
            CenterWindow();
    
            // set icons
    
            HICON hIcon = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON));
    
            SetIcon(hIcon, TRUE);
    
            HICON hIconSmall = AtlLoadIconImage(IDR_MAINFRAME, LR_DEFAULTCOLOR, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON));
    
            SetIcon(hIconSmall, FALSE);
    
            //subclassing an Edit control
    
            m_NumberOnlyEdit.SubclassWindow(GetDlgItem(IDC_EDIT_NUMBER));
    
            m_NumberOnlyEdit.SetWindowText(_T("NumberOnlyEdit Subclassing"));
    
            int lineCount = m_NumberOnlyEdit.GetLineCount();
    
            return TRUE;
    
        }
    
        void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    
        {
    
            //Only number or backspace is allowed
    
            if (('0'<=nChar && nChar<='9') || (nChar==VK_BACK))
    
            {
    
                //Accept the default process
    
                SetMsgHandled(FALSE);   //or DefWindowProc();
    
            }
    
            else
    
            {
    
                ::MessageBeep(MB_ICONERROR);    //Warning
    
            }
    
        }
    
        //...
    
    };

    其中CContainedWindowT<CEdit>在内部生成了一个新类,并将所有的内部窗口消息都转发给了控件的父窗口(即对话框),因此可以将特殊消息处理代码放在父窗口的消息映射列表中,这样,就没有必要自己单独创建一个新类了。

    注意,这种处理方法需要我们制定一个消息映射的分支编号(本例子中为12),这是一个任意选定的编号(父窗口中可能存在多个分支编号,以分别处理各个控件转发过来的消息):

    CMainDlg()
    
        : m_NumberOnlyEdit(this,12)
    
    {
    
        //...
    
    }
    
    BEGIN_MSG_MAP_EX(CMainDlg)
    
        //...
    
        ALT_MSG_MAP(12)  //Process message from m_NumberOnlyEdit control
    
            MSG_WM_CHAR(OnChar)
    
    END_MSG_MAP()

    三.一些结论

    有上可知,窗口子类化可以实现窗口超类化相同的功能,且更加灵活。

    如果控件的父窗口为普通窗口或父控件窗口,可以使用窗口超类化;如果父窗口是对话框,需要对对话框模板中已存在的控件行为进行定制,可以使用窗口子类化。

    从这里可以看到WTL的美妙之处,既提供足够的抽象(面向对象,模板化),有可以非常容易的直接使用Windows的底层技术,非常方便的与Win32 API 交互,其消息映射也非常灵活。

    总之,学了WTL之后就再也不想回头用MFC了Winking smile

  • 相关阅读:
    fastjson报错 java.lang.StackOverflowError
    关于mybatis使用foreach插入速度较慢的问题
    selectKey返回查询的LAST_INSERT_ID的总是1
    使用java制作https证书
    cf 809
    多校 2009 3
    多校 2009 2
    多校 2009 1
    codeforces 808
    hdu 4734 数位DP
  • 原文地址:https://www.cnblogs.com/fwycmengsoft/p/2579619.html
Copyright © 2011-2022 走看看