Visual C++ tutorial
Control ตอน 3 (กลไก DDX และ DDV)

DDX และ DDV คืออะไร?
DDX และ DDV คือกลไกที่ MFC ได้จัดเตรียมให้ เพื่ออำนวยความสะดวก ในการโอนย้ายข้อมูล และตรวจสอบ ความถูกต้อง ของข้อมูล ระหว่าง control และ ตัวแปร ที่ใช้เก็บข้อมูล ของ control นั้น โดย

หลักการง่าย ๆ ก็คือ control แต่ละตัว จะมีตัวแปรประจำ control นั้น ๆ ซึ่งตัวแปรนี้ ทำหน้าที่ ในการเก็บค่า ข้อมูลหรือสภาวะของ control นั้น ๆ ไว้ และจะมีฟังก์ชั่นหนึ่ง ที่ทำหน้าที่เป็นตัวเชื่อม (link) ตัวแปรที่กำหนดไว้เข้ากับ control ที่ต้องการ รวมถึงกำหนด การตรวจสอบ ความถูกต้อง ของข้อมูล และท้ายที่สุด เมื่อต้องการจะโอนย้าย ข้อมูล ก็จะมีการเรียกใช้ฟังก์ชั่นเพื่อให้มีการโอนย้ายข้อมูล โดยส่งพารามิเตอร์กำหนดทิศทาง ของ การโอนย้ายข้อมูล ไปด้วย ก็จะทำให้การโอนย้ายข้อมูลสำเร็จ ได้โดยง่าย

ขั้นตอนการเขียนโปรแกรม ให้ใช้กระบวนการ DDX และ DDV

1. กำหนดตัวแปรประจำ control
ตัวแปรนี้ จะใช้เก็บข้อมูลประจำ control นั้น โดยเราจะกำหนดตัวแปรนี้ ในตอนประกาศคลาสของ Dialog box ที่มี control นั้น วางอยู่ ซึ่งชนิดของ ตัวแปร จะขึ้นอยู่กับชนิดของ control ด้วย โดยถ้าเป็น Check box ก็จะมีตัวแปรที่เก็บข้อมูลเป็นชนิด int, ถ้าเป็น Edit control ก็จะมีตัวแปรประจำ เป็นชนิด CString หรือ int เป็นต้น สมมติว่า Dialog box ของเรา มี control ที่ต้องการทำ DDX อยู่ 3 ชนิด ดังแสดงในรูป

การประกาศคลาสของ Dialog box นี้ จะกำหนดค่าตัวแปรประจำ control ดังต่อไปนี้

class CMyDialog:public CDialog
{
 public:
        CString EditData;      // ตัวแปรที่ใช้เก็บข้อความใน Edit box
        int CheckStatus1;      // ตัวแปรใช้เก็บสภาวะของ Check box 1
        int CheckStatus2;      // ตัวแปรใช้เก็บสภาวะของ Check box 2
        int RadioStatus;        // ตัวแปรใช้เก็บหมายเลขสภาวะของกลุ่ม Radio button 1, 2, 3

   //..........................              // ประกาศ ค่าอื่น ๆ ในคลาส

 DECLARE_MESSAGE_MAP() // ประกาศการ Map message
};

เพื่อน ๆ จะสังเกตเห็นว่า Edit box และ check box แต่ละตัว จะมีตัวแปรประจำอยู่ แต่สำหรับ Radio button 3 ตัว จะมีตัวแปรเก็บสภาวะ เพียงตัวเดียวคือ RadioStatus ที่เป็นเช่นนี้เพราะ ในขณะใดขณะหนึ่ง Radio button ทั้ง 3 ตัวนี้ จะมีเพียงตัวเดียว เท่านั้น ที่จะอยูในสภาวะ check ได้ ดังนั้น จึงไม่จำเป็น ที่จะต้องตรวจสอบ สภาวะของ Radio button ทุกตัว ขอเพียงรู้ว่า Radio button ตัวไหน อยู่ในสภาวะ check ก็เพียงพอแล้ว เพราะแสดงว่า Radio button ตัวอื่น ๆ ที่เหลือ ต้องอยู่ในสภาวะ clear อย่างแน่นอน ดังนั้นใช้ตัวแปร เก็บสภาวะ RadioStatus เพียงตัวเดียว ก็เพียงพอแล้ว โดยถ้า RadioStatus = 0 แสดงว่า Radio button 1 อยู่ในสภาวะ check , แต่ถ้า RadioStatus = 1 ก็แสดงว่า Radio button 2 อยู่ในสภาวะ check, เป็นเช่นนี้ไปเรื่อย ๆ แต่ถ้าต้องการ ให้ Radio button ทุกตัว อยู่ในสภาวะ clear ก็ให้กำหนดค่า RadioStatus = -1 เสีย

2. ประกาศฟังก์ชั่น DoDataExchange() และทำการ override ฟังก์ชั่นนี้
ฟังก์ชั่น DoDataExchange() จะเป็นเหมือน ฟังก์ชั่นที่ใช้บรรจุ ข้อมูลที่บอกว่า ตัวแปรตัวไหนจะสัมพันธ์กับ control ตัวไหน โดยเราจะประกาศ ฟังก์ชั่นนี้ ในตอนประกาศคลาส ของ Dialog box ดังโค้ดต่อไปนี้

 class CMyDialog:public CDialog
{
 public:
        CString EditData;      // ตัวแปรที่ใช้เก็บข้อความใน Edit box
        int CheckStatus1;      // ตัวแปรใช้เก็บสภาวะของ Check box 1
        int CheckStatus2;      // ตัวแปรใช้เก็บสภาวะของ Check box 2
        int RadioStatus;        // ตัวแปรใช้เก็บหมายเลขสภาวะของกลุ่ม Radio button 1, 2, 3

   //..........................              // ประกาศ ค่าอื่น ๆ ในคลาส

  public:
        void DoDataExchange(CDataExchange*  pDX); // ประกาศฟังก์ชั่น DoDataExchange()

 DECLARE_MESSAGE_MAP() // ประกาศการ Map message
};

จากนั้น ในตอนประกาศรายละเอียด ของคลาส CMyDialog เราก็จะเขียน รายละเอียดของโค้ด ที่ใช้เชื่อมโยงตัวแปร ที่ต้องการเข้ากับ control ที่กำหนด ซึ่งจะกำหนดไว้ในฟังก์ชั่น DoDataExchange() ดังต่อไปนี้

void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
  DDX_Text (pDX,IDC_EDIT1,EditData); // เชื่อมโยงตัวแปร EditData เข้ากับ Edit control
  DDX_Check (pDX,IDC_CHECK1,CheckStatus1); // เชื่อมโยงตัวแปร CheckStatus1 เข้ากับ Check box control1
  DDX_Check (pDX,IDC_CHECK2,CheckStatus2); // เชื่อมโยงตัวแปร CheckStatus2 เข้ากับ Check box control2
  DDX_Radio (pDX,IDC_RADIO1,RadioStatus);    // เชื่อมโยงตัวแปร RadioStatus เข้ากับกลุ่มของ Radio button

  CDialog::DoDataExchange(pDX);
}

ฟังก์ชั่นที่ใช้เชื่อมโยง ตัวแปร เข้ากับ control ที่กำหนดนั้น มีหลายฟังก์ชั่น แต่ที่สำคัญมีดังต่อไปนี้
DDX_Text เชื่อมโยงตัวแปรชนิด int, float, หรือ CString เข้ากับ Edit control
DDX_Check เชื่อมโยงตัวแปรชนิด int เข้ากับ Check box control
DDX_Radio เชื่อมโยงตัวแปรชนิด int เข้ากับ กลุ่มของ Radio button controls
DDX_LBIndex เชื่อมโยงตัวแปรชนิด int เข้ากับ List box control
DDX_CBIndex เชื่อมโยงตัวแปรชนิด int เข้ากับ Combo box control
DDX_Scroll เชื่อมโยงตัวแปรชนิด int เข้ากับ Scroll bar

รูปแบบของฟังก์ชั่น DDX เหล่านี้จะคล้าย ๆ กัน คือจะมีรูปแบบดังนี้
void AFXAPI DDX_Function( CDataExchange* pDX, int nIDC, int& value );
โดยที่ Function คือฟังก์ชั่น DDX ที่แสดงในตารางข้างต้น
  pDX ตัวแปรชนิด pointer ที่จะชี้ไปยังวัตถุเป้าหมายของคลาส CDataExchange
  nIDC คือหมายเลข ID ของ control ที่ต้องการ
  value คือตัวแปร ที่ต้องการให้เชื่อมโยงกับ control นั้น ๆ

3. สั่งให้มีการโอนย้ายข้อมูลระหว่างตัวแปรกับ control เมื่อต้องการ
ฟังก์ชั่นที่จะใช้สั่งให้มีการโอนย้ายข้อมูลระหว่างตัวแปรกับ control คือ ฟังก์ชั่น UpdateData() ซึ่งมีรูปแบบดังนี้
BOOL UpdateData( BOOL bSaveAndValidate = TRUE );
โดยที่ bSaveAndValidate คือ แฟลกที่จะกำหนดทิศทางของการโอนย้ายข้อมูล

โดยทิศทางการโอนย้ายข้อมูล จะเป็นดังแสดงในรูป
UpDateData(FALSE);
UpDateData(TRUE);

โดยเมื่อไหร่ ที่มีการเรียกใช้ฟังก์ชั่น UpdateData(FALSE); ฟังก์ชั่นนี้ ก็จะมีการสร้างวัตถุเป้าหมาย (object) ของคลาส CDataExchange ขึ้นมา แล้วจึงเรียกใช้ฟังก์ชั่น DoDataExchange(); โดยอัตโนมัติ และทำการส่งค่าตัวชี้ไปยัง object ของคลาส CDataExchange (pDX) ไปให้ด้วย ทำให้ฟังก์ชั่น DDX ภายในฟังก์ชั่น DoDataExchange ทำการย้ายค่าจากตัวแปรไปยัง control ที่สัมพันธ์กัน เมื่อเสร็จสิ้น กระบวนการนี้ ก็จะทำให้ control แต่ละตัวมีค่าตามตัวแปรที่สัมพันธ์กับ control นั้น
แต่ถ้า มีการเรียกใช้ฟังก์ชั่น UpdateData(TRUE); ก็จะเกิดกระบวนการ ดังข้างต้นเช่นกัน แต่ทิศทางของการย้ายข้อมูล จะเป็นจาก control ไปยัง ตัวแปรที่สัมพันธ์กัน ซึ่ง เมื่อเสร็จสิ้นกระบวนการ ก็จะทำให้ ตัวแปรที่กำหนด ได้รับค่าจาก control ที่สัมพันธ์กัน

ตัวอย่างโปรแกรม
เรามาลองเขียนโปรแกรมตัวอย่าง เพื่อเพิ่มความเข้าใจกันนะคะ โดยโปรแกรมของเรา จะมีลักษณะ คล้าย ๆ กับโปรแกรมที่เขียนขึ้น ในบทที่แล้ว นะคะ เพื่อให้สามารถเปรียบเทียบ วิธีการโอนย้ายข้อมูลแบบเขียนโค้ดเอง กับใช้กระบวนการ DDX ว่าแตกต่างกันอย่างไร โดยทำตามขั้นตอน ต่อไปนี้ค่ะ

1. สร้างโปรแกรมใหม่ ให้ เป็นแบบ Win 32 application แล้วปรับให้โปรแกรม สามารถใช้ MFC โดยเลือกเมนู Project -> Setting เลือกหัวข้อ Use MFC in share dll หรือ Use MFC in static libraly ก็ได้

2. สร้าง Dialog box โดยเลือกเมนู Project -> Add to project เลือก เมนู New แล้วเลือก Resource script ใส่ชื่อรีซอส์ทไฟล์ เป็น resource.rc ก็จะได้รีซอส์ทไฟล์ขึ้นมา จากนั้นก็ให้ เพิ่ม Dialog box เข้าไปในรีซอสท์ไฟล์ โดยเลือกเมนู Insert -> Resource เลือกหัวข้อ เป็น Dialog box แล้วคลิ๊ก New จากนั้นก็สร้าง Dialog box ขึ้นมา 2 ชุด โดยชุดแรก ให้มีลักษณะดังแสดงในรูป

Dialog box 1

Dialog box ชุดแรกจะมี หมายเลข ID เป็น IDD_DIALOG1 มี caption เป็น DDX main dialog และมี control บรรจุอยู่ โดยให้เพื่อน ๆ กำหนดหมายเลข ID ของ control แต่ละตัว ดังแสดงในตารางต่อไปนี้
ชนิดของ Control
Caption
หมายเลข ID
Edit box Edit box IDC_EDIT1
Check box Check box 1 IDC_CHECK1
Check box Check box 2 IDC_CHECK2
Radio button Radio button 1 IDC_RADIO1
Radio button Radio button 2 IDC_RADIO2
Radio button Radio button 3 IDC_RADIO3
Button Control Input Dialog IDC_INPUT


ต่อไป เป็นขั้นตอนสำคัญ คือการกำหนดกลุ่มให้ Radio button อยู่ในกลุ่มเดียวกัน เพื่อให้สามารถใช้ตัวแปร เพียงตัวเดียว บอกถึง สภาวะของ Radio button ทั้ง 3 ตัวได้ว่า ตัวไหนอยู่ใน สภาวะ Check โดยการจะทำเช่นนี้ได้ เราจะต้องรู้ถึง tab order ของ control ใน dialog box นี้เสียก่อน โดยให้เพื่อน ๆ เลือกเมนู LayOut -> Tab Order ก็จะได้ลำดับของ tab order แสดง เป็นตัวเลขติดอยู่กับ control ดังแสดงในรูป เราสามารถเปลี่ยนลำดับของ Tab order เสียใหม่ก็ได้ โดยการเอาเมาส์ ไปคลิ๊กที่ control เรียงลำดับกันใหม่ control เหล่านั้น ก็จะมีหมายเลข Tab order เรียงกันไป ตามลำดับของเมาส์ที่เรานำไปคลิ๊กที่ control แต่ในตอนนี้ เราจะไม่เปลี่ยน Tab order ของ control เหล่านี้นะคะ (แต่ control ของเพื่อน ๆ อาจมี Tab order ที่ไม่ตรงกับรูปก็ได้ค่ะ ก็ไม่ต้องกังวลใจนะคะ ของเพียงแต่ให้ Tab order ของ Radio button ทั้งกลุ่ม เรียงลำดับกันก็พอแล้วค่ะ)



จะเห็นว่า Tab order ของ Radio button 1 ถึง 3 ก็คือ 8, 9, 10 การที่เราจะกำหนดกลุ่ม ให้กับ Radio button นั้น เราทำได้โดยกำหนด window style ให้เป็น WS_GROUP ปิดหัวปิดท้าย กลุ่มของ radio button ที่เราต้องการ ดังนั้น เราจะกำหนด WS_GROUP ให้กับ radio button 1 และ กำหนด WS_GROUP ให้กับ control ตัวที่มี tab order อยู่ถัดจาก radio button ตัวสุดท้ายของกลุ่ม ซึ่งก็คือ group box ที่มี caption เป็น radio button status นั้นเอง ซึ่งจะมี tab order เป็น 11 ซึ่งมีขั้นตอนดังนี้

กำหนด WS_GROUP ให้กับ Radio button 1 (Tab order เป็น 8) โดยคลิ๊กขวาที่ Radio button1 แล้วเลือกหัวข้อ properties แล้ว ทำเครื่องหมาย "check" ที่หน้าหัวข้อเลือก group ดังรูป


จากนั้นให้กำหนด WS_GROUP ให้กับ Group box (Tab order เป็น 11) โดยคลิ๊กขวาที่ Group box นั้น แล้วเลือกหัวข้อ properties แล้วทำเครื่องหมาย "check" ที่หน้าหัวข้อเลือก group เช่นกัน


คราวนี้เรามาดู ไฟล์รีซอร์ท ที่เก็บ รายละเอียดของ control ใน Dialog box 1 นี้ไว้กัน โดย เลือกคลิ๊กที่เมนู File -> Open เลือกไฟล์ชื่อ resource.rc และเลือกหัวข้อ Open As เป็น "Text" แล้ว คลิ๊ก Open ให้เลื่อนไปหา Resource script ของ Dialog box ที่มี ID เป็น IDD_DIALOG1 จะเห็น resource script คล้าย ๆ กับรูปที่แสดงด้านล่าง


จะเห็นว่า มีการเพิ่ม WS_GROUP ให้กับ Radio button ที่มี ID เป็น IDC_RADIO1 ซึ่งเป็น control ตัวแรกในกลุ่ม Radio button ของเรา และมีการเพิ่ม WS_GROUP ให้กับ Group box ที่มี ID เป็น IDC_STATIC ซึ่งเป็น control ตัวแรก ที่อยู่ถัดจากกลุ่มของ Radio button ของเรา เท่านี้ก็เท่ากับว่าเราได้กำหนด WS_GROUP ปิดหัวปิดท้ายให้กับกลุ่ม Radio button ของเรา เรียบร้อยแล้วค่ะ

ต่อไปเราก็จะมาสร้าง Dialog box 2 กัน โดยใช้วิธีเดียวกันกับ ที่ใช้สร้าง Dialog box 1 ค่ะ

Dialog box 2


Dialog box ชุดที่สอง จะมี หมายเลข ID เป็น IDD_DIALOG2 มี caption เป็น Input dialog และมี control บรรจุอยู่ โดยให้เพื่อน ๆ กำหนดหมายเลข ID ของ control แต่ละตัว ดังแสดงในตารางต่อไปนี้
ชนิดของ Control
Caption
หมายเลข ID
Edit box Edit box IDC_EDIT2
Check box Check box 1 IDC_CHECK3
Check box Check box 2 IDC_CHECK4
Radio button Radio button 1 IDC_RADIO4
Radio button Radio button 2 IDC_RADIO5
Radio button Radio button 3 IDC_RADIO6

จากนั้นตรวจสอบ Tab order ของ dialog box 2 จะได้ดังรูป


แล้วกำหนด WS_GROUP ปิดหัวปิดท้าย กลุ่มของ Radio button ด้วยวิธีข้างต้น ก็จะได้ดังรูปต่อไปนี้

ปิดหัว ที่ Radio button 1 (IDC_RADIO4)


ปิดท้าย ที่ Group box "Check box Status" (IDC_STATIC)


จัดการ Save ข้อมูลทั้งหมดโดยเลือกเมนู Save -> Save All

3. เขียนโค้ดโปรแกรม ส่วน ที่ควบคุมโปรแกรมทั้งหมด ซึ่งจะเก็บไว้ในไฟล์ชื่อ myapp.h และ myapp.cpp ซึ่งไม่มีอะไรเพิ่มเติมเป็นพิเศษ ดังแสดงต่อไปนี้

myapp.h
// myapp.h
class CMyApp:public CWinApp
{
	public:
		BOOL InitInstance();
};

myapp.cpp
//myapp.cpp
#include <afxwin.h>
#include "myapp.h"
#include "maindialog.h"

CMyApp app; // สร้างวัตถุเป้าหมายของ Application class เป็นจุดเริ่มต้นทำงานของโปรแกรม

BOOL CMyApp::InitInstance()
{
	CMainDialog d;
	m_pMainWnd = &d;

	d.DoModal();

	return FALSE;
}

จากนั้นก็เขียนโค้ดส่วนของ Dialog box 1 ซึ่งเป็น Dialog หลักของโปรแกรมของเรา ซึ่งจะเก็บไว้ในไฟล์ชื่อ maindialog.h และ maindialog.cpp

maindialog.h
// maindialog.h
class CMainDialog:public CDialog
{
	protected:
				CString MainString; // ตัวแปรชนิด CString ใช้เก็บข้อความใน Edit box
				int		Checkbox1_status; // ตัวแปรชนิด int ใช้เก็บสภาวะของ check box 1
				int		Checkbox2_status;  // ตัวเปรชนิด int ใช้เก็บสภาวะของ check box 2
				int		Radio_Status;  // ตัวแปรชนิด int ใช้เก็บสภาวะของ Radio button 1,2,3
	public:
			CMainDialog(); // function constructor
			void DoDataExchange(CDataExchange* pDX); // ประกาศฟังก์ชั่นเพื่อ override
			afx_msg void OnInput(); // ฟังก์ชั่นที่ตอบสนองต่อการกดปุ่ม Input Dialog

			DECLARE_MESSAGE_MAP()
};


maindialog.cpp
// maindialog.cpp
#include <afxwin.h>
#include "maindialog.h"
#include "inputdialog.h"
#include "resource.h"

BEGIN_MESSAGE_MAP(CMainDialog,CDialog)
  // ดักจับการกดปุ่ม Input Dialog เพื่อเรียกใช้ ฟังก์ชั่น OnInput()
	ON_BN_CLICKED(IDC_INPUT,OnInput) 
END_MESSAGE_MAP()

CMainDialog::CMainDialog():CDialog(IDD_DIALOG1,NULL)
{
}

// ฟังก์ชั่นนี้ จะถูกเรียกใช้โดยอัตโนมัติ เมื่อมีการเรียกใช้ฟังก์ชั่น UpdateData()
void CMainDialog::DoDataExchange(CDataExchange* pDX)
{
	DDX_Text(pDX, IDC_EDIT1, MainString); // เชื่อมโยงตัวแปร MainString เข้ากับ Edit box1 ของ Dialog 1
	DDX_Check(pDX, IDC_CHECK1,Checkbox1_status); // เชื่อมโยงตัวแปร Checkbox1_status เข้ากับ checkbox 1 ของ Dialog1
	DDX_Check(pDX, IDC_CHECK2,Checkbox2_status); // เชื่อมโยงตัวแปร Checkbox2_status เข้ากับ checkbox 2 ของ Dialog1
	DDX_Radio(pDX, IDC_RADIO1,Radio_Status); // เชื่อมโยงตัวแปร Radio_Status เข้ากับกลุ่มของ Radio button ของ Dialog 1

	CDialog::DoDataExchange(pDX); // เรียกใช้ ฟังก์ชั่น DoDataExchange ของ base class ด้วย
}

// ฟังก์ชั่นนี้ตอบสนองต่อการคลิ๊กปุ่ม Input Dialog
afx_msg void CMainDialog::OnInput()
{
	UpdateData(); // มีค่า Default เป็น TRUE จึงทำการโอนย้ายข้อมูลจาก control ไปยัง ตัวแปรของ Main Dialog

	CInputDialog dialog; // สร้างวัตถุเป้าหมายของ คลาส CInputDialog

 // นำค่าตัวแปรต่าง ๆ ของ Main Dialog ส่งไปให้กับตัวแปร ใน Input Dialog
	dialog.CheckBox1_Status = Checkbox1_status; 
	dialog.CheckBox2_Status = Checkbox2_status;

	dialog.Radio_Status = Radio_Status;

	dialog.inputdata = MainString;

 // สั่งให้ Input Dialog ทำงาน เมื่อทำงานเสร็จแล้ว ถ้ามีการส่งค่า IDOK กลับมา ให้ทำงานในวงเล็บ
	if(dialog.DoModal() == IDOK) 
	{
      // โอนย้ายค่าจากตัวแปรใน Input Dialog ไปให้กับตัวแปรใน Main Dialog
		Checkbox1_status = dialog.CheckBox1_Status;
		Checkbox2_status = dialog.CheckBox2_Status;

		Radio_Status = dialog.Radio_Status;

		MainString = dialog.inputdata;

		UpdateData(FALSE); // ทำการโอนย้ายข้อมูลจาก ตัวแปรไปยัง control ของ Main Dialog
	}
}


โค้ดส่วนสุดท้าย ก็เป็นโค้ดโปรแกรมในส่วนของ Dialog box 2 หรือ Input Dialog นั่นเอง โดยจะอยู่ในไฟล์ชื่อ inputdialog.h และ inputdialog.cpp ดังต่อไปนี้

inputdialog.h
// inputdialog.h
class CInputDialog:public CDialog
{
	public:
			CString inputdata; // ตัวแปรชนิด CString ใช้เก็บข้อความใน Edit box
			int		CheckBox1_Status; // ตัวแปรชนิด int ใช้เก็บสภาวะของ check box 1
			int		CheckBox2_Status; // ตัวเปรชนิด int ใช้เก็บสภาวะของ check box 2
			int		Radio_Status;  // ตัวแปรชนิด int ใช้เก็บสภาวะของ Radio button 1,2,3

	public:
			CInputDialog();  // function constructor
			void	DoDataExchange(CDataExchange* pDX);// ประกาศฟังก์ชั่นเพื่อ override

			DECLARE_MESSAGE_MAP()
};

inputdialog.cpp
// inputdialog.cpp
#include <afxwin.h>
#include "inputdialog.h"
#include "resource.h"

BEGIN_MESSAGE_MAP(CInputDialog,CDialog)

END_MESSAGE_MAP()

CInputDialog::CInputDialog():CDialog(IDD_DIALOG2,NULL)
{
}

// ฟังก์ชั่นนี้ จะถูกเรียกใช้โดยอัตโนมัติ เมื่อมีการเรียกใช้ฟังก์ชั่น UpdateData()
void CInputDialog::DoDataExchange(CDataExchange* pDX)
{
	DDX_Text(pDX, IDC_EDIT2, inputdata); // เชื่อมโยงตัวแปร inputdata  เข้ากับ Edit box ของ Dialog 2
	DDX_Check(pDX, IDC_CHECK3, CheckBox1_Status); // เชื่อมโยงตัวแปร Checkbox1_Status เข้ากับ checkbox 1 ของ Dialog2
	DDX_Check(pDX, IDC_CHECK4, CheckBox2_Status); // เชื่อมโยงตัวแปร Checkbox2_Status เข้ากับ checkbox 2 ของ Dialog2
	DDX_Radio(pDX, IDC_RADIO4, Radio_Status); // เชื่อมโยงตัวแปร Radio_Status เข้ากับกลุ่มของ Radio button ของ Dialog 2

	CDialog::DoDataExchange(pDX); // เรียกใช้ ฟังก์ชั่น DoDataExchange ของ base class ด้วย
}


4. Compile โปรแกรม แล้วลอง Run โปรแกรม จะได้ผลลัพธ์ ดังแสดงต่อไปนี้



เมื่อเราทำการพิมพ์ข้อความ ลงใน Edit box หรือ คลิ๊กเลือก check box หรือ radio button ใด ๆ ก็ตามใน main dialog พอเราคลิ๊กเพื่อ เปิด input dialog ขึ้นมา จะเห็นว่าข้อมูลจาก control ใน main dialog ถูกส่งไปให้กับ control ใน input dialog เรียบร้อยแล้ว ดังแสดง ในรูปด้านล่าง


เมื่อ input dialog แสดงออกมาแล้ว เราสามารถ เปลี่ยนแปลง และแก้ไข ค่าใน control ของ input dialog ได้ตามใจชอบ



และเมื่อไหร่ ที่เราคลิ๊กเลือกปุ่ม OK ใน input dialog ก็จะทำให้ค่าใน control ของ input dialog ถูกส่งกลับไป ให้ control บน main dialog ดังแสดงในรูปด้านล่าง แต่ถ้าเราคลิ๊กปุ่ม Cancel ค่าใน control ของ main dialog จะไม่มีการเปลี่ยนแปลง



โค้ดคำสั่ง ส่วนที่ทำกระบวนการ DDX ของ main dialog จะอยู่ในไฟล์ maindialog.cpp ดังต่อไปนี้ ค่ะ

// ฟังก์ชั่นนี้ตอบสนองต่อการคลิ๊กปุ่ม Input Dialog
afx_msg void CMainDialog::OnInput()
{
	UpdateData(); // มีค่า Default เป็น TRUE จึงทำการโอนย้ายข้อมูลจาก control ไปยัง ตัวแปรของ Main Dialog

	CInputDialog dialog; // สร้างวัตถุเป้าหมายของ คลาส CInputDialog

 // นำค่าตัวแปรต่าง ๆ ของ Main Dialog ส่งไปให้กับตัวแปร ใน Input Dialog
	dialog.CheckBox1_Status = Checkbox1_status; 
	dialog.CheckBox2_Status = Checkbox2_status;

	dialog.Radio_Status = Radio_Status;

	dialog.inputdata = MainString;

 // สั่งให้ Input Dialog ทำงาน เมื่อทำงานเสร็จแล้ว ถ้ามีการส่งค่า IDOK กลับมา ให้ทำงานในวงเล็บ
	if(dialog.DoModal() == IDOK) 
	{
      // โอนย้ายค่าจากตัวแปรใน Input Dialog ไปให้กับตัวแปรใน Main Dialog
		Checkbox1_status = dialog.CheckBox1_Status;
		Checkbox2_status = dialog.CheckBox2_Status;

		Radio_Status = dialog.Radio_Status;

		MainString = dialog.inputdata;

		UpdateData(FALSE); // ทำการโอนย้ายข้อมูลจาก ตัวแปรไปยัง control ของ Main Dialog
	}

จะเห็นว่า เมื่อเราคลิ๊กปุ่ม Input dialog บน main dialog จะมีการเรียกฟังก์ชั่น OnInput() ขึ้นมาทำงาน ซึ่งในตอนแรก จะไปเรียกใช้ ฟังก์ชั่น UpdateData( ); ฟังก์ชั่นนี้ ถ้าไม่ใส่พารามิเตอร์ให้ ก็จะถือว่า มีพารามิเตอร์เป็น TRUE เสมอ ในกรณีนี้ จึงเป็นการสั่งให้ โอนย้ายข้อมูล จาก control ใน main dialog ไปยังตัวแปรของ main dialog
จากนั้นก็จะมีการสร้าง วัตถุเป้าหมาย ของ input dialog แล้วทำการ copy ค่าของตัวแปรของ main dialog ไปให้กับตัวแปรของ input dialog จากนั้นก็เรียกให้ input dialog ทำงาน โดยใช้ฟังก์ชั่น DoModal() ฟังก์ชั่น นี้จะไม่คืนการทำงาน กลับมาให้ main dialog เลย จนกว่า จะปิด input dialog นี้เสียก่อน
เมื่อปิด input dialog ลง จะมีการส่งค่ากลับมาให้ main dialog ด้วย โดยถ้า input dialog นี้ถูกปิดลง ด้วยการคลิ๊กที่ปุ่ม OK ก็จะมีการส่งค่า IDOK กลับมา แต่ถ้าถูกปิดด้วยการ คลิ๊กที่ปุ่ม Cancel ก็จะมีการส่งค่า IDCANCEL กลับมาให้ ซึ่ง main dialog จะตรวจสอบว่าค่า ที่ส่งกลับมาเป็น IDOK หรือไม่? ถ้าเป็น IDOK ก็จะทำการ copy ค่าของตัวแปรใน input dialog กลับไปให้กับตัวแปร ของ main dialog จากนั้นจึงเรียกใช้ ฟังก์ชั่น UpdateData(FALSE) เพื่อนำค่าจากตัวแปร ไปแสดงออกที่ control ของ main dialog

ข้อสังเกต เพื่อน ๆ สังเกตเห็นอะไรอย่างหนึ่งไม๊คะ ว่าในโค้ดโปรแกรมส่วนของ input dialog นั้น ไม่มีการเรียกใช้ ฟังก์ชั่น UpdateData() เลย แล้วทำไม ค่าในตัวแปรของ input dialog จึงถูกส่งออกไปยัง control ของ dialog ได้ สาเหตุ เป็นเพราะ เมื่อใดก็ตามที่มี การสร้าง Dialog box ขึ้นมา จะมีการเรียกใช้ฟังก์ชั่น OnInitDialog() โดยอัตโนมัติ ซึ่งถ้าเราไม่ได้มีการ override ฟังก์ชั่นนี้แล้ว ก็จะทำให้มีการเรียกใช้ฟังก์ชั่น ของคลาสฐานแทน ซึ่งก็คือ CDialog::OnInitDialog() นั่นเอง ซึ่งภายในฟังก์ชั่นนี้ จะทำให้มีการโอนย้ายข้อมูลจากตัวแปรไปยัง control ใน dialog box โดยอัตโนมัติ
เมื่อผู้ใช้คลิ๊ก OK จะทำให้ไปเรียก CDialog::OnOK() ซึ่งจะทำการโอนย้ายข้อมูล จาก control ไปยังตัวแปรใน dialog box โดยอัตโนมัติเช่นกัน แต่ถ้าคลิ๊ก Cancel จะไม่ทำให้เกิดการโอนย้ายข้อมูล
ดังนั้น ถ้าเพื่อน ๆ เขียนโค้ดโปรแกรม ให้มีการใช้กลไก DDX นี้ และต้องมีการ override ฟังก์ชั่น OnInitDialog() และ ฟังก์ชั่น OnOK() ด้วยแล้ว ก็อย่าลืมเพิ่มบรรทัด การเรียกใช้ฟังก์ชั่น ของ Base class ลงไปด้วยนะคะ ไม่เช่นนั้น จะทำให้ กลไก DDX ทำงานไม่ถูกต้องได้ค่ะ

BOOL CInputDialog::OnInitDialog()
{
 //........เพิ่มการประกาศค่าอื่น ๆ
 CDialog::OnInitDialog(); // เรียก ฟังก์ชั่น OnInitDialog() ของ Base class ด้วย
 }


afx_msg void CInputDialog::OnOK()
 {
  //.......เพิ่มการทำงานอื่น ๆ
  CDialog::OnOK(); // เรียกฟังก์ชั่น OnOK() ของ Base class ด้วย
  }

พูดมาตั้งเยอะ ยังไม่ได้พูดถึง DDV เลยเจ้าค่ะ เอาเป็นว่ายกยอด ไปพูดในตอนหน้าแล้วกันนะคะ สวัสดีค่ะ


หนูแจ๋ว

  
Hosted by www.Geocities.ws

1