Visual C++
มารู้จักกับ Message Box และ menu
วินโดวส์ได้ จัดเตรียมส่วนประกอบ (component) ต่าง ๆ ที่ใช้ในการ ติดต่อกับผู้ใช้งาน ในบรรดา ส่วนประกอบเหล่านี้ ส่วนประกอบ ที่เราพบได้บ่อยที่สุด คือ Message Box และ menu
Message Box คืออะไร?
message box หรือ "กล่องข้อความ" ก็คือหน้าต่างวินโดวส์ หน้าต่างหนึ่ง ที่จะปรากฎออกมา เพิ่อบอก ข้อมูลบางอย่าง ให้ผู้ใช้งานได้รับรู้ และ message box นี้จะยังคงปรากฏอยู่ จนกว่าผู้ใช้ จะกดปุ่มใดปุ่มหนึ่ง ใน message box ก็จะทำให้ message box นี้หายไป และจะมีการคืนค่า ของปุ่มที่กด มาให้โปรแกรมของเรา รู้ว่า ผู้ใช้กดปุ่มอะไร เพื่อจะได้ตอบสนองได้ อย่างถูกต้อง
การจะสร้าง message box ให้แสดงออกมา บนจอภาพ ทำได้ง่าย ๆ ด้วยฟังก์ชั่น MessageBox( ) ซึ่งเป็น ฟังก์ชั่นสมาชิกของคลาส CWnd ซึ่งมีรูปแบบ ของฟังก์ชั่นดังนี้

        int CWnd::MessageBox(	LPCSTR lpszText, 
		        	LPCSTR lpszTitle = NULL, 
		        	UINT MBType = MB_OK);
ยกตัวอย่าง เช่น ถ้าเราใช้คำสั่ง MessageBox("My simple Message box"); ก็จะทำให้เกิด message box ดังแสดงในรูป

จะเห็นว่าคำสั่ง MessageBox ข้างต้น จะมีพารามิเตอร์ให้เพียงตัวเดียว คือ lpszText ซึ่งก็คือข้อความ " My simple Message box" นั่นเอง ส่วนพารามิเตอร์ที่เหลือ ไม่ได้พิมพ์ลงไป จึงใช้ค่า default ก็คือ lpszTitle ก็มีค่าเป็น NULL เป็นผลให้ บน Title bar มีข้อความ VCP7 ซึ่งเป็นชื่อของ ไฟล์ .cpp ของโปรแกรมนี้ ปรากฎอยู่ ส่วนค่า default ของ MBType ก็คือ MB_OK จึงทำให้มีปุ่มกด OK อยู่เพียงปุ่มเดียว ค่า MBType ที่ใช้กำหนดชนิด ของ message box จะมีอยู่หลายค่า ซึ่งแบ่งได้เป็น 2 กลุ่ม คือ
กลุ่มที่ใช้กำหนด ชนิดของปุ่มกดบน Message box
MB_OK มีปุ่มกด OK เพียงปุ่มเดียว
MB_OKCANCEL มีปุ่มกด OK และปุ่ม CANCEL
MB_YESNO มีปุ่มกด YES และปุ่ม NO
MB_YESNOCANCEL มีปุ่มกด YES, ปุ่ม NO, และปุ่ม CANCEL
MB_RETRYCANCEL มีปุ่ม RETRY และปุ่ม CANCEL
MB_ABORTRETRYIGNORE มีปุ่ม ABORT, ปุ่ม RETRY, และปุ่ม IGNORE

กลุ่มที่ใช้กำหนด ชนิดรูปไอคอน (icon) บน Message box
MB_ICONEXCLAMATION แสดงรูป
MB_ICONINFORMATION แสดงรูป
MB_ICONQUESTION แสดงรูป
MB_ICONSTOP แสดงรูป

ค่า MBType ทั้งสองกลุ่มนี้ สามารถ นำมาใช้งาน ร่วมกันได้ โดยการนำค่า มา OR กัน โดยใช้เครื่องหมาย ( | ) เช่นถ้าใช้คำสั่ง

MessageBox("Are you sure?","Warnning",MB_YESNO|MB_ICONQUESTION);

ก็จะทำให้ได้ MessageBox แสดงบนจอดังรูปข้างล่าง

จะเห็นว่า Message Box ที่ได้จะมีข้อความว่า "Are you sure?" อยู่ และมีข้อความว่า "Warnning" บน Title bar ของ message box และมีปุ่มกด YES และปุ่ม NO รวมทั้งมีรูปเครื่องหมายคำถาม (?) อยู่ด้วย
message box เมื่อปรากฎขึ้นมาแล้ว จะคงอยู่จนกว่าเราจะตอบสนอง ด้วยการกดปุ่มใดปุ่มหนึ่งบน message box นั้น จึงจะทำให้ message box นั้นปิดไป และจะมีการคืนค่าของปุ่มที่กด กลับมาให้โปรแกรมของเราด้วย ซึ่งค่าที่คืนมาให้นี้จะอยู่ใน รูปของตัวแปรจำนวนเต็ม (integer) ซึ่งเราจะนำไปใช้งานได้ตามต้องการ โดยวินโดวส์ได้กำหนดชื่อเฉพาะ ให้กับค่าของปุ่มกด ใน message box ไว้ดังต่อไปนี้
ปุ่มกด ค่าที่ส่งกลับ
Abort IDABORT
Cencel IDCANCEL
Ignore IDIGNORE
Yes IDYES
No IDNO
Retry IDRETRY
OK IDOK

นี่เป็นรายละเอียดคร่าว ๆ เกี่ยวกับ Message box นะคะ คราวนี้ เราจะมาทำความรู้จักกับ Menu กัน แล้วจึง ใช้ menu ในการเรียก message box ให้แสดงออกมาทางจอภาพ

menu คืออะไร?
menu เป็นองค์ประกอบหนึ่งของ สิ่งที่เรียกว่า resource โดย Resource คือสิ่ง ที่ถูกใช้โดยโปรแกรมของเรา แต่ถูกกำหนดไว้นอกส่วนของโค้ดโปรแกรม โดย resource อาจจะมีทั้ง menu, icon, dialog box, หรือ bitmap graphics ก็ได้
โดย resource จะถูกสร้างแยกต่างหาก จากโปรแกรมของเรา และจะถูกนำไปรวมกับโปรแกรม ในขั้นตอนการ link เพื่อให้ได้โปรแกรม .EXE ในที่สุด
เราสามารถสร้างไฟล์ resource ได้โดยใช้โปรแกรม text editor ธรรมดาก็ได้ โดยให้เป็น ไฟล์ที่มีนามสกุลเป็น .RC (resource script) หรืออาจใช้โปรแกรมที่เรียกว่า resource editor ที่มีมากับ visual studio เป็นตัว สร้างไฟล์ resourec script ก็ได้เช่นกัน ซึ่งจะสะดวกมากกว่า ซึ่งในบทความนี้เรา จะสร้างเมนู โดยใช้ resource editor เพื่อความสะดวก และรวดเร็ว โดยขั้นตอนการสร้าง และติดตั้งเมนู ลงในโปรแกรมของเราจะมีดังต่อไปนี้
  1. ใช้ resourec editor สร้าง menu แล้วเก็บไว้ในไฟล์ .RC
  2. โหลด menu เข้าไปในโปรแกรม ในตอนใช้คำสั่ง Create สร้างหน้าต่างวินโดวส์ของเรา
  3. สร้าง message map เพื่อตอบสนอง ต่อการเลือก menu ของเรา
คราวนี้เรา มาลองทำการสร้างเมนูกันนะคะ โดยตอนแรกให้ เปิดโปรแกรม Visual C++ ขึ้นมา แล้วสร้างโปรเจคท์ใหม่ ขึ้นมา โดยใช้คำสั่ง File->New คลิ้กเลือกแทป Project เลือกสร้างโปรเจคท์ เป็น Win32 application ตั้งชื่อโปรเจคท์ เป็น simplemenu ก็ได้ค่ะ จากนั้น ให้เลือกชนิดของโปรเจ็ค เป็นแบบ empty project ซึ่งจะต้อง เขียนโค้ดโปรแกรมเองทั้งหมด แล้วคลิ้ก finish ก็เป็นอันเสร็จสิ้นการ สร้างโปรเจคท์ใหม่
จากนั้นก็ เลือกเมนู Project->setting จะได้ Dialog box ขึ้นมา ที่หัวข้อ Microsoft Foundation Class ให้เลือก Use MFC in a Shared DLL เพื่อให้ โปรเจคท์ใช้ความสามารถ ของ MFC ได้
ต่อไปนี้ล่ะค่ะ ที่เราจะเริ่มสร้าง menu กันโดย เลือกเมนู Project->Add to Project ->New ดังรูป


จากนั้น เลือกหัวข้อ resource script เพื่อเป็นการเพิ่มไฟล์ resource script เข้าไปในโปรเจคท์ โดยตั้งชื่อเป็น ไฟล์ simplemenu.rc แล้วกด OK

จะเห็นว่า ในส่วนของ source file จะมีไฟล์ simplemenu.rc เพิ่มขึ้นมา

แต่ไฟล์ simplemenu.rc นี้ยังเป็นไฟล์เปล่า ๆ อยู่ เราจะต้อง ทำการเพิ่ม menu เข้าไปใน resource file นี้ โดยเลือกเมนู insert->resource เพื่อแทรก resource เข้าไปในไฟล์ simplemenu.rc

ในช่อง resource type ให้เลือกเป็น menu เพราะเราจะเพิ่ม menu เข้าไปใน resource file แล้วกด New

หน้าจอ จะเปลี่ยนไปเป็น menu editor โดยมีแถบสีเทา เกิดขึ้น พร้อมกับมีช่องสี่เหลี่ยม อยู่ด้านซ้ายสุด เป็นตำแหน่ง ของเมนูแรก ของโปรแกรมเรา

ให้ดับเบิ้ลคลิ๊ก ที่ช่องสี่เหลี่ยมนั้น ก็จะได้ dialog box ของ คุณสมบัติของ menu (menu item property) ขึ้นมา ให้สังเกตว่า มีเครื่องหมาย ตรง check box ของ Pop-up เพื่อบอกว่า เมนูนี้เป็น pop-up menu ซึ่งจะเป็นตัวเรียก เมนูย่อยอื่น ๆ ขึ้นมาเท่านั้น แต่จะไม่มีการส่ง message ใด ๆ กลับมาให้โปรแกรมของเรา จึงไม่จำเป็นต้องมี หมายเลย ID ดังนั้นในช่อง ID ของเมนูนี้ จะเป็นสีเทา ไม่สามารถตั้งชื่อ ID ให้กับเมนูนี้ได้ แต่เราสามารถตั้ง ชื่อ ที่จะแสดงบนเมนูย่อยนี้ได้ ดังแสดงในช่อง Caption โดยตั้งชื่อเป็น &Message box เครื่องหมาย & ที่อยู่หน้าตัวอักษร M จะทำให้ ตัว M มีการขีดเส้นใต้ และสามารถใช้คีย์ Alt+M เพื่อเรียกเมนูนี้ออกมาได้

จากนั้น ให้คลิ๊กตรงช่องสี่เหลี่ยม ด้านล่างของ เมนู Message box ก็จะได้ dialog box ของเมนูย่อย ออกมา โดยเมนูย่อยนี้ จะไม่มีเครื่องหมายตรง check box ของ Pop-up แสดงว่า เมนูย่อยนี้ จะสามารถส่ง message ไปให้หน้าต่างโปรแกรมของเราได้ ให้เราตั้งชื่อ Caption ของเมนูนี้เป็น &Yes-no box และตั้งชื่อ ID (ที่จะส่งกลับ เมื่อมีการเลือกเมนูนี้) ให้เป็น IDM_YESNO (เราสามารถตั้งเป็นชื่ออื่นได้ตามใจชอบ แต่ควรให้ขึ้นต้นด้วย IDM_ เพื่อ บอกให้รู้ว่าเป็น ID ของ Menu

แล้วใช้วิธีเดียวกัน ในการสร้างเมนูย่อย ที่ชื่อว่า Yes-no-&Cancel box และมี ID เป็น IDM_YESNOCANCEL กับเมนูย่อย ที่ชื่อ &Abort-retry-ignore box และมี ID เป็น IDM_ABORTRETRYIGNORE ตามลำดับ



คราวนี้เราจะมาทำเมนู เพื่อออกจากโปรแกรมนี้กัน โดยให้คลิ๊กเมาส์ ตรงช่องสี่เหลี่ยมด้านขวาบน ของเมนูบาร์ จะได้ Dialog box ออกมา ซึ่ง จะมีเครื่องหมายตรง check box Pop-up และช่อง ID จะเป็นสีเทา เพราะเมนูที่อยู่บนเมนูบาร์ ส่วนใหญ่มัก จะเป็นเมนู ที่ใช้เรียกเมนูอื่น อีกทีหนึ่ง (pop-up menu) แต่เราต้องการให้ เมนูนี้ส่ง message ให้หน้าต่างโปรแกรม ของเรา จึงต้องเอาเครื่องหมายตรง check box Pop-up ออก จะทำให้ช่อง ID สามารถป้อนค่าลงไปได้ ให้เราตั้งชื่อเมนูนี้เป็น E&xit และตั้ง ID เป็น IDM_EXIT ดังแสดงในรูป



แล้วจึงมาคลิ๊กเลือกเมนู File -> Save all เพื่อบันทึกเมนูที่เราสร้างขึ้นนี้ เก็บไว้ในไฟล์ simplemenu.rc


จากนั้น คลิ๊กเลือกเมนู File -> Open จะเห็นว่าในโปรเจคท์ของเรา นอกจากจะมี ไฟล์ simplemenu.rc แล้ว ยังมีไฟล์ resource.h อยู่อีกด้วย เดี๋ยวเราจะมาดูว่าไฟล์นี้ ถูกสร้างขึ้นมาได้อย่างไร


คลิ๊กเลือกที่ไฟล์ simplemenu.rc เพื่อจะเปิดไฟล์นี้ โดยให้เลือกที่ list box open as: ให้เป็น Text เพราะ เราจะดู รายละเอียดของไฟล์นี้ ในรูปโค้ดโปรแกรมกัน แล้วคลิ๊กปุ่ม Open (****อย่าลืม! ก่อนที่จะเปิดไฟล์นี้ เราต้องปิด menu editor เสียก่อน โดยคลิ๊กที่เครื่องหมาย X บนมุมบนขวาสุด ของ menu editor เพราะ ไฟล์นี้ จะสามารถเปิดแบบ Text ได้ จะต้องปิดไฟล์เดียวกัน ที่เปิดในแบบ wysiwyg (what you see is what you get) ให้เรียบร้อยเสียก่อน ******)


ในส่วนต้นของ ไฟล์ simplemenu.rc จะมีการ include ไฟล์ที่สำคัญไว้สองไฟล์คือ afxres.h ซึ่งเป็นไฟล์ ที่ใช้กำหนดค่าต่าง ๆ ให้กับ resource ของ MFC ส่วนอีกไฟล์หนึ่งก็คือไฟล์ resource.h ซึ่ง menu editor จะสร้างขึ้นในตอนที่เรา กำหนดค่าต่าง ๆ ให้กับเมนูที่จะสร้าง


และในไฟล์ simplemenu.rc จะต้องมีโค้ดคำสั่งต่อไปนี้อยู่ ซึ่งเป็นคำสั่งในการสร้างเมนูนั่นเอง


รูปแบบคำสั่ง resource script ในการสร้าง menu จะเป็นดังนี้

      MenuName MENU [option]
      BEGIN
               menuitem
      END
   
MenuName คือชื่อของ menu นี้ ซึ่งในที่นี้ menu editor ตั้งชื่อให้ว่า IDR_MENU1
[Option] คือคำสั่งเพิ่มเติม ในที่นี้คือ DISCARDABLE ซึ่งหมายความว่า
เมนูนี้จะถูกถอดออกจากหน่วยความจำ เมื่อไม่ใช้งาน
menuitem จะมี menuitem ได้ 2 แบบ คือ POPUP และ MENUITEM
โดยมีรูปแบบคำสั่งดังนี้
MENUITEM ใช้สร้างเมนูที่คลิ๊กแล้วจะไปเรียกฟังก์ชั่นการทำงานต่าง ๆ
โดยมีรูปแบบคำสั่งคือ MENUITEM "itemname", MenuID
โดย "itemname" คือชื่อที่จะปรากฎอยู่บนเมนู
และ MenuID คือค่าคงที่เฉพาะของเมนูนั้น เช่น IDM_YESNO
POPUP ใช้สร้างเมนูที่ ภายในบรรจุเมนูย่อยอื่น ๆ ไว้
โดยมีรูปแบบคำสั่งคือ POPUP "Popupname"
โดย "Popupname" คือชื่อที่จะปรากฎอยู่บนเมนู

คราวนี้เราลอง มาดูรายละเอียดของไฟล์ resource.h ที่ถูกสร้างขึ้นโดยอัตโนมัติ เมื่อเราสร้างไฟล์ simplemenu.rc ขึ้น โดยคลิ๊กที่เมนู File -> Open แล้วเลือกที่ไฟล์ resource.h แล้วคลิ๊ก Open

จะเห็นว่าภายในไฟล์ resource.h จะมีการ กำหนดค่าคงที่ ที่ใช้แทนค่า ID ต่าง ๆ ของเมนูที่เราตั้งชื่อไว้ ในตอนสร้างเมนู นี่เป็นสาเหตุ ที่มีการสร้างไฟล์ resource.h ขึ้นมาโดยอัตโนมัติ ก็เพื่อใช้เป็นไฟล์ที่เก็บค่าคงที่ต่าง ๆ ที่เรากำหนดขึ้น ในตอนสร้างรีซอสท์ไฟล์นั่นเอง จะสังเกตว่าค่าคงที่ ที่ถูกกำหนดให้โดยอัตโนมัตินี้ จะไม่มีค่าซ้ำกันเลย และมีค่ามากกว่า 100 และไม่เกิน 65,565 และเราต้องมีการ include ไฟล์ rosource.h นี้เข้าไปในไฟล์ .cpp ที่มีการอ้างถึงค่า ID ของ รีซอรสท์ที่เราสร้างขึ้นด้วยทุกไฟล์ ไม่เช่นนั้นจะเกิด error ขึ้นในขั้นตอนการคอมไพล์โปรแกรมได้

คราวนี้เรา จะสร้าง header file ที่ใช้ประกาศคลาส ของโปรแกรมเรากัน โดยคลิ๊กที่เมนู File -> New เลือกแทป Files คลิ๊กเลือก C/C++ Header file ตั้งชื่อว่า simplemenu.h แล้วคลิ๊ก OK

จากนั้นเขียนโค้ดต่อไปนี้ ลงไปในไฟล์ simplemenu.h

/* simplemenu.h */
/* main window class */
class CMainWin: public CFrameWnd
{
public:
	CMainWin( );                             /* function constructor */
	afx_msg void OnYesNoBox( ); /* ประกาศ function prototype */
	afx_msg void OnYesNoCancelBox( );
	afx_msg void OnAbortRetryIgnoreBox( );
	afx_msg void OnExit( );

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

/* application class */
class CApp: public CWinApp
{
public:
	BOOL InitInstance( );
};
จากนั้นให้สร้าง ไฟล์ simplemenu.cpp โดยคลิ๊กที่เมนู File -> New คลิ๊กเลือกแทป Files แล้วเลือกหัวข้อ C++ Source File ตั้งชื่อไฟล์เป็น simplemenu.cpp แล้วคลิ๊ก OK

แล้วเขียนโค้ดต่อไปนี้ลงไป ในไฟล์ simplemenu.cpp

/* file simplemenu.cpp */
#include <afxwin.h>
#include <afxres.h>  /* ต้อง include ไฟล์นี้ ในทุกไฟล์ที่ใช้งานรีซอรท์ */
#include "simplemenu.h"
#include "resource.h"  /* ต้อง include ไฟล์นี้ ในทุกไฟล์ ที่อ้างถึง ID ของ รีซอรท์ */

CMainWin::CMainWin( )
{
	Create(NULL,
                           "Simple Menu",
                           WS_OVERLAPPEDWINDOW,
                           rectDefault,
                           NULL,
                           MAKEINTRESOURCE(IDR_MENU1));  /* กำหนดค่าเมนู ให้กับ พารามิเตอร์ตัวที่6 ของฟังก์ชั่น Create( )*/ 
}

BOOL CApp::InitInstance( )
{
	m_pMainWnd = new CMainWin( );
	m_pMainWnd ->ShowWindow(m_nCmdShow);
	m_pMainWnd ->UpdateWindow( );

	return TRUE;
}

/*message map */
BEGIN_MESSAGE_MAP(CMainWin,CFrameWnd)
	ON_COMMAND(IDM_YESNO,OnYesNoBox)  /* มาโครที่ใช้ตอบสนอง ต่อการเลือกเมนู */
	ON_COMMAND(IDM_YESNOCANCEL,OnYesNoCancelBox)
	ON_COMMAND(IDM_ABORTRETRYIGNORE,OnAbortRetryIgnoreBox)
	ON_COMMAND(IDM_EXIT,OnExit)
END_MESSAGE_MAP( )

/* yes-no box function */
    afx_msg void CMainWin::OnYesNoBox( )
               {
	int button;  /* กำหนดตัวแปร button เป็นชนิด interger */
                 /* สร้าง Message box แล้วรอรับค่าปุ่มที่ถูกกด แล้วส่งค่าปุ่มที่ถูกกดบน message box กลับมาเก็บในตัวแปร button */
	button = MessageBox("Press yes or no?","YESNO box",MB_YESNO|MB_ICONQUESTION);
	if(button==IDYES)
		MessageBox("You press yes button",NULL,MB_OK); แสดง Message box นี้เมื่อปุ่ม OK ถูกกด */
	else
		MessageBox("You press no button"); แสดง Message box นี้เมื่อปุ่ม CANCEL ถูกกด */
                }



/* yes-no-cancel function */
afx_msg void CMainWin::OnYesNoCancelBox( )
{
	int button;
	button = MessageBox("Select one button","YESNOCANCEL BOX",MB_YESNOCANCEL);
	switch(button)
	{
		case IDYES:	MessageBox("You select Yes button");
				break;
		case IDNO:	MessageBox("You select No button");
				break;
		case IDCANCEL:	MessageBox("You select Cancel button");
				break;
	}
}

/* abort-retry-ignore function */
afx_msg void CMainWin::OnAbortRetryIgnoreBox( )
{
	int button;
	button = MessageBox("Select one button","Abort Retry Ignore Box",MB_ABORTRETRYIGNORE);
	switch(button)
	{
		case IDABORT:	MessageBox("You select Abort button");
				break;
		case IDRETRY:	MessageBox("You select Retry button");
				break;
		case IDIGNORE:	MessageBox("You select Ignore button");
				break;
	}
}

/* exit function */
afx_msg void CMainWin::OnExit( )
{
	DestroyWindow( ); /* ปิด โปรแกรมเรา*/
}

CApp myApp; /*สร้าง object ของ คลาส CApp ขึ้นมา ซึ่งเป็นจุดเริ่มต้นการทำงานของโปรแกรมของเรา*/
เมื่อพิมพ์โค้ดโปรแกรมเสร็จเรียบร้อยแล้ว ก็ให้เซฟไฟล์ทั้งหมด โดยคลิ๊กที่เมนู File -> Save All จากนั้นก็ทำการ สร้างโปรแกรมโดยใช้คำสั่ง BUILD->BUILD simplemenu.exe หรือกดปุ่ม F7 หรือใช้ Toobar ดังที่ได้อธิบายไปแล้วในบทที่ 3 ก็ได้ตามอัธยาศัยค่ะ
คราวนี้ เราลองมาทดสอบโปรแกรมของเรากัน โดยคลิ๊กที่เมนู Build->Execute simplemenu.exe หรือกดปุ่มที่ Toolbar ดังแสดงในบทที่ 3 แล้วดูผลที่เกิดขึ้น ก็จะได้หน้าต่าง โปรแกรมของเรา ดังแสดงในรูปข้างล่างนี้ค่ะ

ซึ่งเมื่อเราคลิ๊กที่เมนู "Message box" ก็จะได้เมนู pop up แสดงขึ้นมา แล้วเราลองเลื่อนเมาส์ ไปเลือก menu item ที่ชื่อ "Yes-no-cancel box" ก็จะได้ Message box ที่มีปุ่ม 3 ปุ่ม และข้อความดังแสดงข้างล่างนี้

และถ้าเราคลิ๊กเลือกปุ่ม Yes ก็จะทำให้ได้ Message Box ที่มีข้อความว่า "you select Yes button" แสดงออกมา

การทำงานดังตัวอย่างข้างต้น เป็นการทำงานตามฟังก์ชั่น OnYesNoCancelBox( ) ที่ได้กำหนดไว้ในโค้ดโปรแกรมข้างต้นนะคะ

และถ้าเราคลิ๊กที่ เมนู Exit ก็จะมีการเรียกฟังก์ชั่น OnExit( ) มาทำงาน ซึ่งในฟังก์ชั่นนี้ มีค่าสั่งอยู่คำสั่งเดียวคือ DestroyWindow( ); ซึ่งจะทำให้ ปิดโปรแกรมของเรา ซึ่งจะมีผลเหมือนการคลิ๊กที่ปุ่ม X ด้านบนขวาของ title bar เพื่อปิด โปรแกรมเหมือนกันค่ะ


เราจะมาลองดูรายละเอียดของคำสั่งต่าง ๆ ในบทนี้เพิ่มเติมนะคะ โดยในตอนแรก เมื่อเราสร้างเมนู โดยใช้ menu editor แล้ว ก็จะได้ไฟล์ออกมา 2 ไฟล์ คือ เมื่อสร้างเมนู เสร็จเรียบร้อยแล้ว ก็จะนำเมนูไปติดตั้งเข้ากับ หน้าต่างของโปรแกรมของเรา ทำได้ง่าย ๆ โดย ใช้คำสั่งมาโคร MAKEINTRESOURCE( ชื่อเมนูที่ถูกกำหนดไว้ใน resource file) เพิ่มลงไปให้เป็นพารามิเตอร์ตัวที่ 6 ของคำสั่ง Create( ) ที่จะสร้างหน้าต่างวินโดวส์ของเรา เราลองมาดูรายละเอียดของคำสั่งนี้ อีกครั้งนะคะ

	BOOL CFrameWnd::Create(	LPCSTR ClassName, 
			            	LPCSTR Title,
				DWORD Style,
				RECT     size,
				CWnd    *Parent,
				LPCSTR MenuName,
				DWORD ExStyle,
				CCreateContext *Context
				);
ClassName เป็นตัวชี้ ที่ชี้ไปยัง คลาส ที่เก็บรูปแบบ ของหน้าต่างวินโดวส์ของเรา ปกติจะกำหนดเป็น NULL เพื่อให้ใช้ default class ที่สร้างขึ้นโดย MFC
Title เป็นตัวชี้ไปยังตำแหน่งที่เก็บ ชื่อที่จะปรากฏบน title bar ของหน้าต่างโปรแกรมของเรา
Style เป็นตัวกำหนดรูปแบบของหน้าต่างวินโดวส์เรา ซึ่งมีมาโครหลายตัวที่ใช้กำหนดรูปแบบ เช่น
  • WS_OVERLAPPEDWINDOW หน้าต่างวินโดวส์จะมี system menu, มีขอบ, มีปุ่มย่อ, ปุ่มขยาย, และปุ่มปิดหน้าต่างวินโดวส์
  • WS_OVERLAPPED หน้าต่างวินโดวส์จะมี ขอบ ที่ใช้เมาส์ลากปรับขนาดหน้าต่างได้
  • WS_MAXIMIZEBOX มีปุ่มขยายหน้าต่างวินโดวส์
  • WS_MINIMIZEBOX มีปุ่มย่อหน้าต่างวินโดวส์
  • WS_SYSMENU มีปุ่ม system menu อยู่ด้านซ้ายบน ของหน้าต่างวินโดวส์
  • WS_HSCROLL มี scroll bar ในแนวนอน ตรงด้านล่างของ หน้าต่างวินโดวส์
  • WS_VSCROLL มี scroll bar ในแนวตั้ง ตรงด้านขวา ของหน้าต่างวินโดวส์
มาโครข้างต้น สามารถนำมา OR กันได้ โดยใช้เครื่องหมาย | เพื่อให้ได้ รูปแบบหน้าต่างวินโดวส์ ตามที่เราต้องการ
Size เป็นตัวแปรโครงสร้างชนิด RECT ที่ใช้กำหนด โคออดิเนต ของหน้าต่างวินโดวส์ ที่จะแสดงบนจอภาพ โดยตัวแปร RECT จะมีโครงสร้างดังนี้
		typedef struct tagRECT {
				       LONG left;
				       LONG top;
				       LONG right;
				       LONG bottom;
				       } RECT;
	
ตัวอย่างต่อไปนี้ เป็นการสร้าง หน้าต่างวินโดวส์ ให้ตำแหน่งบนซ้าย อยู่ที่ตำแหน่ง (10,10) และตำแหน่ง ล่างขวา ของหน้าต่างวินโดวส์ อยู่ที่ตำแหน่ง (200,200) ของจอภาพ โดยจะเขียนโค้ดไว้ใน ส่วนของ function constructor ของคลาส CMainWin
		CMainWin::CMainWin( )
		{
		    RECT r;
		    r.top = 10; 
		    r.left = 10;
		    r.bottom = 200; 
		    r.right = 200;

		    Create(NULL,"Define Window size",WS_OVERLAPPEDWINDOW,r);
                                  }
	
*Parent เป็นตัวชี้ ที่ชี้ไปยัง หน้าต่างวินโดวส์แม่ (Parent window) ของหน้าต่างวินโดวส์ของเรา ซึ่งในกรณีนี้ หน้าต่างวินโดวส์ ของเราเป็นหน้าต่างแรกสุด จึงไม่มีหน้าต่างแม่ ต้องกำหนดพารามิเตอร์นี้ เป็น NULL
MenuName ***เป็นชื่อของ menu ที่ต้องการ ติดตั้งเข้าไปใน หน้าต่างวินโดวส์ของเรา ชื่อของเมนูเราจะอยู่ใน ไฟล์ .RC และต้องใช้มาโคร MAKEINTRESOURCE(ชื่อเมนู) ในการกำหนดพารามิเตอร์ตัวนี้ด้วย เพราะชื่อเมนูเรา ถูก menu editor กำหนดเป็นค่าคงที่แบบ integer ***
ExStyle &
*Context
เป็นตัวกำหนดรูปแบบ เพิ่มเติมให้วินโดวส์ ในที่นี้ไม่ได้ใช้ ก็ให้กำหนดเป็น 0 และ NULL ตามลำดับ หรือจะใช้วงเล็บปิด หลังจากป้อน MenuName เสร็จแล้วก็ได้

หลังจากติดตั้งเมนู โดยเพิ่มพารามิเตอร์ MenuName เรียบร้อยแล้ว เราก็จะมาพูดถึง การจัดการกับ message ที่เมนูส่งออกมา เมื่อถูกคลิ๊กเลือก โดยทุกครั้งที่มีการเลือก menuitem ในเมนู ก็จะมีการส่ง message WM_COMMAND ออกมา และจะมีการส่งค่า ID ของ menuitem ที่เราเลือกนั้น ออกมาด้วย เพื่อเป็นตัวบอกว่า เราเลือก menuitem ใด เพื่อที่โปรแกรมเราจะสามารถ ตอบสนองได้อย่างถูกต้อง การจัดการกับ message WM_COMMAND นั้น จะมีขั้นตอนดังต่อไปนี้
1. ประกาศ function ที่จะทำงาน เมื่อมีการเลือก menuitem แต่ละตัว ลงในส่วนของ การประกาศคลาส หน้าต่างวินโดวส์ของเรา

class CMainWin: public CFrameWnd
{
public:
	CMainWin( );                             /* function constructor */
	afx_msg void OnYesNoBox( ); /* ประกาศ function prototype ที่จะตอบสนอง ต่อการเลือก menuitem แต่ละตัว */
	afx_msg void OnYesNoCancelBox( );
	afx_msg void OnAbortRetryIgnoreBox( );
	afx_msg void OnExit( );

	DECLARE_MESSAGE_MAP( ) /* ประกาศ การ map message */
};
2. เพิ่มมาโคร ที่ใช้จัดการ กับ message WM_COMMAND ลงไปใน message map โดยรูปแบบ ของมาโครที่ใช้ จะเป็นดังนี้

ON_COMMAND(ID,Function Handler)

โดยที่ ID ก็คือค่า ID ของ menuitem ที่ตั้งไว้
และ Function handler ก็คือชื่อ function ที่ต้องการให้ทำงาน เมื่อมีการเลือก menuitem นั้น

BEGIN_MESSAGE_MAP(CMainWin,CFrameWnd)
	ON_COMMAND(IDM_YESNO,OnYesNoBox)  /* มาโครที่ใช้ตอบสนอง ต่อการเลือกเมนู */
	ON_COMMAND(IDM_YESNOCANCEL,OnYesNoCancelBox)
	ON_COMMAND(IDM_ABORTRETRYIGNORE,OnAbortRetryIgnoreBox)
	ON_COMMAND(IDM_EXIT,OnExit)
END_MESSAGE_MAP( )
3. เขียน โค้ดรายละเอียด ของฟังก์ชั่นที่ตอบสนองต่อ การเลือก menuitem แต่ละตัว ในที่นี้จะยกตัวอย่าง ของ ฟังก์ชั่น OnYesNoBox( ) ที่จะตอบสนอง ต่อการเลือกเมนู ที่มี ID เป็น IDM_YESNO

    afx_msg void CMainWin::OnYesNoBox( )
               {
	int button;  /* กำหนดตัวแปร button เป็นชนิด interger */
                 /* สร้าง Message box แล้วรอรับค่าปุ่มที่ถูกกด แล้วส่งค่าปุ่มที่ถูกกดบน message box กลับมาเก็บในตัวแปร button */
	button = MessageBox("Press yes or no?","YESNO box",MB_YESNO|MB_ICONQUESTION);
	if(button==IDYES)
		MessageBox("You press yes button",NULL,MB_OK); แสดง Message box นี้เมื่อปุ่ม OK ถูกกด */
	else
		MessageBox("You press no button"); แสดง Message box นี้เมื่อปุ่ม CANCEL ถูกกด */
                }

จากนั้น ก็ใช้ฟังก์ชั่น if ในการเปรียบเทียบค่า ที่ส่งกลับมา โดยถ้าค่าที่ส่งกลับ เป็น IDYES ก็จะสร้าง message box ที่มีข้อความว่า "You press yes button" แล้วรอให้ผู้ใช้กดปุ่ม OK ก็จะออกจากฟังก์ชั่นนี้ แต่ถ้าค่าที่ส่งกลับ เป็นค่าอื่น ก็จะสร้าง message box ที่มีข้อความว่า "You press no button" แล้วรอให้ผู้ใช้กดปุ่ม OK ก็จะออกจากฟังก์ชั่น ไปรอรับ message อื่น ๆ ต่อไป

เป็นยังไงบ้างคะ งงหรือเปล่า? ถ้างง ก็ค่อย ๆ อ่านทบทวนใหม่ช้า ๆ อีกครั้งนะคะ เพราะเนื้อหาในบทนี้ ค่อนข้างจะยาวไปนิด ขอบคุณเพื่อน ๆ ที่ ติดตามอ่านจนจบบทนะคะ แล้วพบกันใหม่คราวหน้าค่ะ

หนูแจ่ว

Hosted by www.Geocities.ws

1