WTL的窗口子类化与超类化

.窗口超类化(superclassing)

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

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

class CNumberOnlyEdit :

    public CWindowImpl

{

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,因此m_NumberOnlyEdit实例只能使用CWindow类的方法,而不能使用CEdit类的函数(操作Edit控件的函数)。如果在代码中要用到CEdit类的函数,可将基类改为:

class CNumberOnlyEdit :

    public CWindowImpl

{

//......

}

 

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

//Get the count of line in Edit box

int lineCount = m_NumberOnlyEdit.GetLineCount();

.窗口子类化(Subclassing)

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

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

class CNumberOnlyEdit :

    public CWindowImpl

{

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();

//...

 

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

class CMainDlg :

    public CDialogImpl,

    public CWinDataExchange

{

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 , WPARAM , LPARAM , BOOL& )

    {

        // 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

{

private:

    CContainedWindowT 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 , WPARAM , LPARAM , BOOL& )

    {

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

注意,这种处理方法需要我们制定一个消息映射的分支编号(本例子中为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()

 

.一些结论

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

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

你可能感兴趣的:(Windows,C/C++,UILite)