• MFC Dialog (DDX)


    [转载自:http://www.mscto.com/cppvc/1541912.html]

    MFC-对话框与数据交换


    对话框是非常重要的一种窗体,就我看过的应用,除了MDI程序,对话框比文档视图框架用得更多。原因大概有两个,首先对话框可以利用资源编辑器来编 辑,这就有了一点快速开发的感觉,尽管比真正的快速开发弱得多;其次,对话框提供一种叫数据交换的方法,让控件与某种数据类型关联起来,比如Edit控件 与CString变量关联,对CString变量的操作间接映射到Edit控件,这种方便性是不言而喻的。

    对话框的创建
    对话框如何创建,它是对SDK的对话框资源的封装,说得简单一点,就是对CreateDlgIndirect的封装。

    对话框的显示几乎是MFC最简单的窗体创建,用下面两句代码就可以做到: 软件开发网 www.mscto.com

    CMFCTest2Dlg dlg;

    dlg.DoModal();

           对话框资源对应一个资源ID,这个ID在对话框类构造时保存:

    CMFCTest2Dlg::CMFCTest2Dlg(CWnd* pParent /*=NULL*/)

    软件开发网 www.mscto.com

        : CDialog(CMFCTest2Dlg::IDD, pParent)

    {

        ... ...

    }

           DoModal只是利用这个ID创建对话框窗口,不过它还要做几件事情,让对话框融入MFC框架,总体上可以描述如下:

    1.         根据资源ID从程序的资源段中读出对话框资源。

    2.         将对话框的Owner窗口置为Disable。

    3.         调用AfxHookWindowCreate监视对话框的创建,让对话框可以进行消息映射。

    4.         调用CreateDlgIndirect创建对话框。

    5.         调用RunModalLoop进入消息循环。

    整个过程就是这样,创建之后与其他窗体类型没有什么区别,只是要操作对话框的控件会有些麻烦,好在MFC提供了数据交换。

    数据交换
           由于MFC的对话框直接封装自CreateDlgIndirect,因此对话框里面的控件并没有转换成相应的控件类,我们只能通过GetDlgItem取得控件句柄,然后使用API来操作这些控件,这又回到SDK时代了。

           为了简化对话框控件的操作,MFC提出了数据交换。这里的数据交换分两种,一种是控件与数据类型的关联;另一种是控件与控件类的关联。    

    控件与数据类型怎么关联起来的呢?全靠CWnd::UpdateData(BOOL bSaveAndValidate)这个成员函数,如果bSaveAndValidate为True,数据从控件赋给类型变量;反之,数据从类型变量赋给控件。

    假设一个CString变量m_Enter与IDC_ENTER的编辑控件关联,则我们只需这样写:

    m_Enter = "Enter";

    UpdateData(FALSE);

           那么m_Enter的值就会赋给IDC_ENTER。看看UpdateData是怎么做到的:

    BOOL CWnd::UpdateData(BOOL bSaveAndValidate)

    { 软件开发网 www.mscto.com

        CDataExchange dx(this, bSaveAndValidate);

        DoDataExchange(&dx);

        return bOK;

    }

           UpdateData创建一个CDataExchange实例,然后调用DoDataExchange进行数据交换。Dx保存着当前的对话框对象,DoDataExchange是一个虚成员函数,对话框要实现数据交换必须覆盖这个函数:

    void CMFCTest2Dlg::DoDataExchange(CDataExchange* pDX)

    {

        CDialog::DoDataExchange(pDX);

        //{{AFX_DATA_MAP(CMFCTest2Dlg)

        DDX_Text(pDX, IDC_ENTER, m_Enter);

        //}}AFX_DATA_MAP

    }

           DDX_Text是类向导自动生成的,实现如下:

    void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value)

    {

        //根据nIDC取得对话框控件

        HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);

        //从控件取得数据赋给Value

    if (pDX->m_bSaveAndValidate)

        {

            int nLen = ::GetWindowTextLength(hWndCtrl);

            ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);

            value.ReleaseBuffer();

        }

        //将值赋给控件

        else

        {

            AfxSetWindowText(hWndCtrl, value);

        }

    }

           DDX_Text仅仅是帮我们调用API,最后将值赋给类型变量, DDX_Text有很多重载形式,使得数据变量可以是Int,Short等其他类型。

     

           数据交换的另一种形式是控件与控件类,比如有一个CButton类m_OK与IDOK关联,CDialog::OnInitDialog会调用UpdateData(FALSE),进而执行流程到DoDataExchange:

    void CMFCTest2Dlg::DoDataExchange(CDataExchange* pDX)

    {

        CDialog::DoDataExchange(pDX);

        //{{AFX_DATA_MAP(CMFCTest2Dlg)

        DDX_Control(pDX, IDOK, m_OK);

        //}}AFX_DATA_MAP

    }

    软件开发网 www.mscto.com

           看看DDX_Control做了什么:

    void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)

    {

        //只子类化一次

        if (rControl.m_hWnd == NULL)    // not subclassed yet

        {

            HWND hWndCtrl = pDX->PrepareCtrl(nIDC);

            rControl.SubclassWindow(hWndCtrl);

        }

    }

           DDX_Control将控件句柄与控件类关联起来,并且只需关联一次,实现关联的方法是子类化hWndCtrl :CWnd::SubclassWindow。这里的子类化其实做了两件事情,一件是通过Attach将控件句柄与控件类映射起来,另一件是通过 SetWindowLong将窗口过程替换成AfxWndProc,使得控件也可以进行消息映射。

    关联以后,m_OK就代表IDOK,以后直接操作m_OK即可。 软件开发网 www.mscto.com

     

           控件关联给我们带来一种非常好的应用,我们在对话框资源上放一个Button的时候,只能处理Click和DbClick两个事件,如果我们想处理 MouseMove消息要怎么做,只能继承CButton写一个派生类,然后在派生类里面处理MouseMove消息,但这样做就不能在资源编辑器上可视 操作按钮了,我们必须手动创建按钮类,写代码控制它的位置,非常地麻烦。

           其实使用数据交换就可以一举两得,让按钮派生类与对话框资源上的按钮关联起来,我们既能扩展按钮的消息处理,又能可视化设计按钮。

           具体是这样做的:

    1.         在对话框资源上放一个按钮IDOK。

    2.         创建一个按钮类派生自CButton,并处理MouseMove消息:

    BEGIN_MESSAGE_MAP(CMyButton, CButton)

        //{{AFX_MSG_MAP(CMyButton)

        ON_WM_MOUSEMOVE()

        //}}AFX_MSG_MAP

    END_MESSAGE_MAP()

    void CMyButton::OnMouseMove(UINT nFlags, CPoint point) 

    {

        MessageBox("Hello");

        CButton::OnMouseMove(nFlags, point);

    软件开发网 www.mscto.com

    }

    3.         利用类向导,将IDOK与一个按钮类关联起来:

    class CMFCTest2Dlg : public CDialog

    {

        ... ...

        CButton m_OK;

        CString m_Enter;

        ... ...

    };

    4.         将CButton设为CMyButton,完毕。

     

    对话框的应用广泛就在于此,RAD其实还是起决定因素的。

  • 相关阅读:
    Go Map
    Go XORM
    Go切片
    Go函数
    dockerfile常用指令
    Goroutine并发控制
    Go 格式转换
    Go 常用知识点及实例
    Go 时间
    Go error
  • 原文地址:https://www.cnblogs.com/taoxu0903/p/1440877.html
Copyright © 2020-2023  润新知