Visual C++
Dialog Box (ภาคสอง)
การสร้าง Application แบบ Dialog Base
การเขียนโปรแกรม บางครั้ง เราไม่จำเป็น ต้องใช้ หน้าต่าง แบบ Frame Window ก็ได้ โดยเฉพาะ เมื่อเป็นโปรแกรม ขนาดเล็ก ที่ไม่ต้องมี การแสดงผล ที่ซับซ้อน เราสามารถ ใช้ Dialog box มาเป็น หน้าต่างหลัก ของโปรแกรมของเราได้ นอกจากนี้แล้ว การใช้ dialog box เป็นหน้าต่างหลัก ยังมีข้อดี อีกประการหนึ่ง คือ เราสามารถจัดวาง ตำแหน่งของ control ต่าง ๆ ไว้บน dialog box ได้อย่างง่ายดาย โดยการใช้ dialog editor
    การเขียนโปรแกรมที่ใช้ Dialog Box เป็นหน้าต่างหลัก
    มีขั้นตอนดังนี้
  1. สร้าง Dialog box จาก Dialog editor โดยจะเก็บไว้เป็นส่วนหนึ่ง ของ Resource file (file นามสกุล .RC)
  2. สร้างคลาส ที่สืบทอดมาจาก คลาส CDialog มาห่อหุ้ม (encapsulate) Dialog box นี้
  3. จัดการกับ message ที่เกิดขึ้นภายใน Dialog box
  4. กระตุ้นให้ Dialog box นี้ทำงาน (Activated a Dialog box)
  5. ปิด Dialog box เมื่อใช้งานเสร็จสิ้น
    ถ้ามองในมุมมองของ OOP แล้ว จะพบว่า โปรแกรมที่ใช้ Dialog box เป็นหน้าต่างหลัก จะประกอบด้วย class อย่างน้อย 2 คลาส คือ
  1. คลาสของโปรแกรม ซึ่งสืบทอดมาจากคลาส CWinApp
  2. คลาสของหน้าต่างหลัก ซึ่งจะสืบทอดมาจากคลาส CDialog
ต่อไป เราลองมาสร้าง โปรแกรม แบบ Dialog base แบบง่าย ๆ เพื่อทำความเข้าใจ กับการทำงานของมันกัน ดังต่อไปนี้นะคะ
1. สร้าง Dialog box จาก Dialog editor
ให้เปิดโปรแกรม Visual C++ ขึ้นมา สร้างโปรเจคท์ใหม่ ตามวิธีการที่ได้กล่าวไปแล้ว ในตอนต้น (**อย่าลืมใช้คำสั่ง Project -> Setting ให้ใช้ MFC ด้วย **) จากนั้น ให้ใช้คำสั่ง Project -> Add to Project -> New เพื่อจะเพิ่มส่วนประกอบ ให้กับโปรเจคท์นี้ ให้เพื่อน ๆ เลือกเพิ่ม Resource script แล้วตั้งชื่อไฟล์ เป็น resource.rc
จากนั้น เลือกเมนู Insert ->Resource เพื่อจะเพิ่มส่วนประกอบ เข้าไปในไฟล์ resource.rc โดยตอนแรก ให้ เลือกเมนู Insert->Resource แล้วเลือกเพิ่ม Dialog จากนั้นกด New ดังแสดงในรูป

ก็จะเข้าสู่ Dialog Editor ซึ่งจะมีการสร้าง Dialog box ให้ พร้อมกับมีปุ่ม OK และ Cancel มาให้ด้วย โดยด้านขวามือ จะมีไดอะล๊อกของ Control ต่าง ๆ ที่เราสามารถ นำไปใส่ไว้ใน Dialog box ที่สร้างขึ้นได้

ลองดับเบิ้ลคลิ๊กที่บริเวณว่าง ๆ ตรงไหนก็ได้ภายใน Dialog box ก็จะได้ Dialog Properties ขึ้นมา โดยในแท็ป General จะมี ID เป็น IDD_DIALOG1 ซึ่งเราสามารถ เปลี่ยนเป็นชื่ออื่นก็ได้ ค่า ID นี้ เราจะนำไป ใช้เป็นพารามิเตอร์ เพื่อส่งผ่านให้กับ class ที่เราสืบทอดมาจาก CDialog เพื่อ ใช้ในการสร้าง Dialog นี้ ออกมาบนหน้าจอต่อไป ส่วนข้อความ ที่อยู่ในช่อง Caption จะเป็นข้อความ ที่จะถูกนำไปแสดง บน Title bar ของ Dialog box นี้ ให้ลองเปลี่ยน เป็นข้อความใด ๆ ก็ได้ ก็จะทำให้ ข้อความบน Title bar ของ Dialog box เปลี่ยนแปลง ตามไปด้วย ดังรูป

จากนั้น ให้ใช้เมาส์ลากแล้ววาง Control ต่าง ๆ บน Dialog box ให้อยู่ในตำแหน่งที่สวยงามดังรูป

จากนั้นเรียกเมนู File->Save All เพื่อบันทึกไฟล์ทั้งหมด เก็บลงในฮาร์ดดิสก์ เราลองมาดูภายใน รีซอร์สไฟล์ ที่เราสร้างขึ้น โดยคลิ๊กที่แท็ป Ressource View ก็จะเห็นว่า ในรีซอร์สไฟล์ ที่ชื่อ resource.rc จะมี Dialog box อยู่ภายใน 1 Dialog ซึ่งมีหมายเลข ID เป็น IDD_DIALOG1, เราสามารถ เปลี่ยนหมายเลข ID เหล่านี้เป็นอะไรก็ได้ ตามใจชอบ โดยใช้วิธีที่ได้กล่าวไปแล้ว ข้างต้น

2. สร้างคลาสที่สืบทอดมาจากคลาส CDialog มาห่อหุ้ม (encapsulate) Dialog box ที่เราสร้างขึ้น
โดยเราจะ เขียนโค้ดโปรแกรม ส่วนของ การประกาศคลาส ไว้ในไฟล์ ที่ชื่อ mydialog.h และเขียนโค้ด ส่วนของ รายละเอียด ของคลาสนี้ ไว้อีกไฟล์หนึ่ง แยกต่างหาก โดยจะเก็บไว้ในไฟล์ชื่อ mydialog.cpp ซึ่งการแยก คลาส แต่ละคลาส ไว้ในไฟล์แต่ละไฟล์นี้ จะช่วยให้สามารถ แก้ไข เพิ่มเติมรายละเอียด ของคลาสใด คลาสหนึ่งได้ง่าย โดย ไม่มีผลกระทบ กับคลาสอื่น ๆ
โดยในส่วนของ ไฟล์ mydialog.h จะประกาศ คลาส CMyDialog ที่สืบทอดมาจาก CDialog ดังนี้

/*File mydialog.h */
/* Dialog derived class */

#include <afxwin.h>
#include "resource.h"

class CMyDialog : public Cdialog
{
  public:
              /* function constructor */
	CMyDialog( );
 	
	/* ประกาศการ map message */
	DECLARE_MESSAGE_MAP()
 };
ในส่วนของไฟล์ mydialog.cpp ก็จะ มีรายละเอียดของ คลาส CMyDialog ไว้ดังนี้
/*File mydialog.cpp */
/* CMyDialog class implementation */
#include "mydialog.h"

/* message map */
BEGIN_MESSAGE_MAP(CMyDialog,CDialog)

END_MESSAGE_MAP()

/* function constructor ของ CMyDialog ซึ่งไปเรียกใช้ function constructor
   ของ CDialog โดยส่ง ID ของไดอะล็อกบอกซ์ ที่สร้างขึ้นไปให้ด้วย */
CMyDialog::CMyDialog():CDialog(IDD_DIALOG1,NULL)
{
}
3. สร้างคลาส ชื่อ CMyApp ซึ่งสืบทอด มาจากคลาส CWinApp เพื่อมาควบคุมการทำงานของโปรแกรม
โดยเราจะสร้างไฟล์ header ขึ้นมาอีก 1 ไฟล์ ให้ชื่อว่า myapp.h เพื่อใช้ประกาศ คลาส CMyApp ดังต่อไป นี้
/*File myapp.h */
/* Application derived class */

class CMyApp:public CWinApp
{
public:
	BOOL InitInstance(); 
};


ในส่วนของ รายละเอียด ของคลาส CMyApp ได้เขียนไว้ในไฟล์ myapp.cpp ดังต่อไปนี้
/*File myapp.cpp*/
/* Application class implementation */

#include <afxwin.h>
#include "myapp.h"
#include "mydialog.h"

CMyApp app;        /* สร้าง ออปเจคท์ ของคลาส CMyApp */

/* ฟังก์ชั่นนี้ จะถูกเรียก โดยอัตโนมัติ เมื่อมีการสร้างออปเจคท์ ของคลาส CMyApp */
BOOL CMyApp::InitInstance() 
{
	/* สร้างออปเจคท์ ของคลาส CMyDialog */
	CMyDialog dlg;	

               /* ส่งค่าแอดเดรสของ ออปเจคท์ ของ CMyDialog ไปให้ตัวแปร m_pMainWnd */ 
	m_pMainWnd = &dlg; 

	/* สร้าง Dialog box แบบ Modal dialog box */
	/* โดยโปรแกรมจะไม่คืนการควบคุม กลับมาให้ คลาส CMyApp อีกเลย*/
                /* จนกว่า จะปิด Dialog box นี้เสียก่อน */
	dlg.DoModal();

	/* เมื่อปิด Dialog box แล้วจึงจะมาทำบรรทัดนี้  ซึ่ง เมื่อค่าที่ส่งกลับจาก
                    ฟังก์ชั่น InitInstance( ) เป็น FALSE ก็จะทำให้ สิ้นสุดการทำงานของ
                    คลาส CMyApp ทำให้สิ้นสุดการทำงานของโปรแกรม */
	return FALSE;
}
จากนั้น ให้ทำการ save โค้ดโปรแกรมทั้งหมด โดยเลือกเมนู File -> Save All จากนั้น ให้สร้างไฟล์ .exe โดย เลือกที่เมนู Build -> Build yourfile.exe หรือกด F7 ก็ได้ จากนั้น ลอง execute โปรแกรม ที่สร้างขึ้น โดย เลือกเมนู Build -> Execute yourfile.exe หรือกด Ctrl + F5 ก็ได้ ก็จะได้รูปหน้าต่าง ของ หน้าต่างโปรแกรม แบบ Dialog base ของเรา ดังแสดง ในรูปต่อไปนี้ ค่ะ


ลอง คลิ๊กที่ปุ่ม OK หรือปุ่ม CANCEL จะเห็นว่า Dialog Box ปิดไป เป็นเพราะอะไร? ทราบไหมคะ? คำเฉลย ก็เป็นเพราะ ตอนสร้าง Dialog box นั้น Dialog editor ได้สร้างปุ่มทั้งสองนี้ ให้เรา โดยอัตโนมัติ และได้กำหนดหมายเลข ID ให้กับปุ่มทั้งสอง นี้เรียบร้อยแล้ว โดย ปุ่ม OK ของเรา จะมีหมายเลข ID เป็น IDOK และปุ่ม CanCel จะมีหมายเลย ID เป็น IDCANCEL ซึ่งเมื่อมีการ คลิ๊กที่ปุ่มทั้ง 2 นี้ ก็จะทำให้ มีการไปเรียก function handler ชื่อ ::OnOK และ ::OnCancel ตามลำดับ ซึ่งจะทำให้ Dialog box นี้ปิดไป กระบวนการนี้ MFC จะจัดการ ให้เราโดยอัตโนมัติ โดยเราไม่ต้อง เขียนโค้ดเลย ถ้าเรา ไม่ต้องการให้ Dialog box ปิดไป ก็ต้องทำการ Override function handler ::OnOK และ ::OnCancel เสีย โดยเราจะมาลอง แก้ไขโค้ดข้างต้น กันดูนะคะ

ขั้นแรก ให้เปิดไฟล์ mydialog.h ขึ้นมา ทำการประกาศ function handler prototype โดยเพิ่มฟังก์ชั่น OnOK() และ OnCancel( ) ลงไป ดังโค้ดต่อไปนี้
/*File mydialog.h */
/* Dialog derived class */

#include <afxwin.h>
#include "resource.h"

class CMyDialog : public Cdialog
{
  public:
              /* function constructor */
	CMyDialog( );
	
	/* ประกาศ function handler prototype  */
	afx_msg void OnOK( );
	afx_msg void OnCancel( );
 	
	/* ประกาศการ map message */
	DECLARE_MESSAGE_MAP()
 };
จากนั้น ก็ทำการเพิ่ม รายละเอียด ของ function handler OnOK( ) และ OnCancel( ) ลงในไฟล์ mydialog.cpp ดังต่อไปนี้
/*File mydialog.cpp */
/* CMyDialog class implementation */
#include "mydialog.h"

/* message map */
BEGIN_MESSAGE_MAP(CMyDialog,CDialog)

END_MESSAGE_MAP()

/* function constructor ของ CMyDialog ซึ่งไปเรียกใช้ function constructor
   ของ CDialog โดยส่ง ID ของไดอะล็อกบอกซ์ ที่สร้างขึ้นไปให้ด้วย */
CMyDialog::CMyDialog():CDialog(IDD_DIALOG1,NULL)
{
}

/* รายละเอียด ของ function OnOK( ) ที่เราทำการ Override */
afx_msg void CMyDialog( )::OnOK( )
{	
    MessageBox(" You select 'OK' button\n and this Dialog won't close");
 }

/* รายละเอียด ของ function OnCancel( ) ที่เราทำการ Override */
afx_msg void CMyDialog( )::OnCancel( )
{
   MessageBox(" You select 'Cancel' button\n and this Dialog will be close");
   
   /* เรียกฟังก์ชั่น OnCancel( ) ของ base class ของคลาส CMyDialog ซึ่งจะทำให้
       Dialog box นี้ปิดไป และคืนค่า IDCANCEL กลับไปให้ โปรแกรมหลัก */
   CDialog::OnCancel( ); 
   }
ลอง compile และ run โปรแกรมนี้อีกครั้ง จะเห็นว่า เมื่อคุณ คลิ๊กที่ปุ่ม OK จะมี Message Box ปรากฎมา บอกว่า "ปุ่ม OK ถูกกด และ Dialog box จะไม่ปิดลง"
แต่ถ้า คุณ คลิ๊กที่ปุ่ม Cancel จะมี Message box แจ้งว่า "ปุ่ม Cancel ถูกกด และจะทำการปิด Dialog box" เมื่อคุณ คลิ๊กปิด Message box นี้ ก็จะทำให้ Dialog box ปิดลงด้วย ดังแสดงในรูป


ด้วยการไป overrile function OnOK( ) และ OnCancel( ) เราก็สามารถ จะกำหนดให้ Dialog box ปิดลง หรือ ไม่ปิดก็ได้ ตามใจเรา และถ้า เราลอง กดปุ่ม ESC บนแป้นพิมพ์ ก็จะเห็นว่า มีการเรียก function handler OnCancel( ) ขึ้นมาด้วย ซึ่งอาจทำให้ เราเผลอ ปิด Dialog box นี้โดยไม่ตั้งใจ ดังนั้น เราอาจเพิ่ม โค้ดโปรแกรม ส่วนที่รับ คำยืนยันจาก ผู้ใช้ว่าต้องการปิด Dialog box หรือไม่ โดยไปแก้ไข function handler OnCancel( ) ในไฟล์ mydialog.cpp เป็นดังนี้

/* รายละเอียด ของ function OnCancel( ) ที่แก้ไขเพิ่มเติม */
afx_msg void CMyDialog( )::OnCancel( )
{
   UINT confirm_value;
   confirm = MessageBox("Do you want to exit?","Exit",MB_YESNO);
   
   /* ตรวจสอบค่าที่ส่งคืนมาจาก MessageBox YESNO ถ้าเป็น IDYES ให้ปิด Dialog box
      ไม่เช่นนั้น ก็ให้ออกจาก function OnCancel( ) แต่ไม่ปิด Dialog box /*
   if(confirm = = IDYES)
       CDialog::OnCancel( );  /* ปิด dialog box */ 
   else
       return;                          /* ออกจาก OnCancel( ) แต่ไม่ปิด dialog box*/
   }
เมื่อเรากด ESC บนแป้นพิมพ์ ก็จะได้ Message box ยืนยันดังแสดงในรูป

ถ้าเรากด Yes ก็จะทำให้ Message box และ Dialog box ปิดลง แต่ถ้ากด No ก็จะทำให้ Message box ปิดลงอย่างเดียว โดยไม่ปิด Dialog box
เรื่องราวของ Dialog box คงต้องขอจบ เพียงเท่านี้นะคะ แล้วคราวหน้า เราจะพูดถึง รายละเอียดของ control ที่สำคัญ ๆ ใน Dialog box กันนะคะ ขอให้มีความสุข กับการเขียนโปรแกรมค่ะ

หนูแจ๋ว

Hosted by www.Geocities.ws

1