MFC控件數據交換:DDX

MFC中操縱控件
   操作控件的兩種方式:
   方式1 通過調用CWnd::GetDlgItem()函數,根據控件ID獲取控件對象指針,操作控件即可。
         對話框的初始化函數:OnInitDialog()
   
   方式2 對話框的數據交換技術(DDX) 
   將控件和一個成員變量綁定,可以通過操作成員變量達到操作控件的目的。
   1 定義與控件綁定的成員變量
   2 在對話框中添加DoDataExchange()函數,在函數中完成控件
     與變量的綁定。
     DDX_Control()//控件類型的綁定
     DDX_Text()//值類型的綁定
   3 通過成員變量完成對控件的操作 
   4 值類型的綁定,還需要調用UpdateData(BOOL)函數
     UpdateData(TRUE)- 控件中的值傳賦值變量(接收)

     UpdateData(FALSE)-將變量的值顯示到控件上

新建一個Win32 Application,選擇A Simple Win32 Application,修改stdafx.h 中的windwos.h爲afxwin.h,工程屬性設置使用MFC靜態庫

編寫如下測試代碼

// DDX.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "resource.h"
class CMyDlg : public CDialog
{
public:
	CMyDlg ():CDialog(IDD_DIALOG1){}
	virtual void DoDataExchange (CDataExchange* pDX);
	virtual BOOL OnInitDialog ();
	virtual void OnOK();
protected:
	CButton m_wndOK;
	CString m_strEdit;
};
void CMyDlg::DoDataExchange (CDataExchange* pDX)
{
	//完成控件與變量的綁定
	DDX_Control (pDX, IDOK, m_wndOK);
	DDX_Text (pDX, IDC_EDIT1, m_strEdit);
}
BOOL CMyDlg::OnInitDialog ()
{
	if (!CDialog::OnInitDialog())
		return FALSE;
	// 初始化控件
	// 方式一:通過GetDlgItem操作控件
	CWnd *pWnd = GetDlgItem (IDCANCEL);
	pWnd->EnableWindow (FALSE);
	m_wndOK.MoveWindow (0, 0, 100, 100);
	m_wndOK.SetWindowText ("DDXOK");

	// 方式二:通過DDX操作控件
	m_strEdit = "Hello you";
	UpdateData (FALSE);
	return TRUE;
}
void CMyDlg::OnOK ()
{
	// 接收控件的值到關聯的變量
	UpdateData (TRUE);
	AfxMessageBox (m_strEdit);
	CDialog::OnOK();
}
class CMyWinApp : public CWinApp
{
public:
	virtual BOOL InitInstance ();
};
CMyWinApp theApp;
BOOL CMyWinApp::InitInstance ()
{
	CMyDlg dlg;
	m_pMainWnd = &dlg;
	dlg.DoModal ();
	return TRUE;
}

DDX的實現原理  

1 控件類型的綁定

DDX_Control (pDX, IDOK, m_wndOK);
跟進:

void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
{// nIDC: IDCOK   , rControl: m_wndOK
	...................................
	// 通過控件ID得到控件句柄
	HWND hWndCtrl = pDX->PrepareCtrl(nIDC);

	if (!rControl.SubclassWindow(hWndCtrl))
	{
		..........................................
	}
}
DDX_Control-->SubclassWindow

跟進:

BOOL CWnd::SubclassWindow(HWND hWnd)
{// this == m_wndOK, hWnd == 控件句柄
	// 將m_wndOK控件變量與OK按鈕句柄綁定
	if (!Attach(hWnd))
		return FALSE;
    ...............................................
}
DDX_Control-->SubclassWindow-->Attach
跟進:

BOOL CWnd::Attach(HWND hWndNew)
{//this == m_wndOK, hWndNew == 控件句柄
    ......................................
	CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist
	ASSERT(pMap != NULL);
    // 建立映射關係
	pMap->SetPermanent(m_hWnd = hWndNew, this);
    ........................................
}
DDX_Control-->SubclassWindow-->Attach-->SetPermanent

跟進:

void CHandleMap::SetPermanent(HANDLE h, CObject* permOb)
{
	.......................................
	// 以控件句柄爲鍵,以變量句柄爲值建立映射關係
	m_permanentMap[(LPVOID)h] = permOb;
	.........................................
}

Ok到這裏就清晰了

  總結流程如下:

   

   DDX_Control(pDX,IDOK,m_wndOK);
   {
     //通過控件ID得到控件句柄
     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
     //將控件句柄與變量綁定
     rControl.SubclassWindow(hWndCtrl);
     {
        Attach(hWnd);
        {
           pMap->SetPermanent(m_hWnd = hWndNew, this);
           {
             //以句柄爲健,以變量地址爲值建立映射關係
             m_permanentMap[(LPVOID)h] = permOb;
           }
        }
     }
   } 

2 值類型的綁定

UpdateData (FALSE);
跟進:

BOOL CWnd::UpdateData(BOOL bSaveAndValidate)
{// this == &dlg,  bSaveAndValidate == FALSE
	.............................................

	CDataExchange dx(this, bSaveAndValidate);

	 ..........................................
	 // 虛函數,會調用我們重寫的函數
	 DoDataExchange(&dx);
	 ........................................
}
UpdateData-->DoDataExchange-->CMyDlg::DoDataExchange-->DDX_Text

跟進:

void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value)
{// pDX中保存了&dlg和FALSE, nIDC == IDOK, value == m_strEdit
	// 通過控件ID拿到編輯框控件句柄
    HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
	if (pDX->m_bSaveAndValidate)
	{// UpdateData (TRUE)
		int nLen = ::GetWindowTextLength(hWndCtrl);
		// 獲取控件文本
		::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
		value.ReleaseBuffer();
	}
	else
	{// UpdateData (FALSE)
		// 設置控件文本
		AfxSetWindowText(hWndCtrl, value);
	}
}

總結上面的流程如下:

   UpdateData(FALSE);
   {
      CDataExchange dx(this, bSaveAndValidate);
      DoDataExchange(&dx);
      {
         DDX_Text(pDX,IDC_EDIT1,m_strEdit);
         {
           //通過控件ID得到控件句柄
           HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
           if (pDX->m_bSaveAndValidate)
	   {
		
		::GetWindowText(hWndCtrl,...); 
		value.ReleaseBuffer();
	   }
	   else
	   {
                //將變量的值設置到控件的窗口上
		AfxSetWindowText(hWndCtrl, value);
	   }
           
         }
      }
   }







發佈了93 篇原創文章 · 獲贊 67 · 訪問量 100萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章