LRESULT SendMessage(
HWND hWnd, // 接收訊息的視窗代碼
UINT Msg, // 發送的訊息
WPARAM wParam, // 訊息的第一個參數
LPARAM lParam // 訊息的第二個參數
);
當應用程式呼叫SendMessage()函式發送訊息時,作業系統首先檢查hWnd參數,找到參數所對應的視窗接著將Msg訊息以及wParam,lParam放到該視窗的訊息佇列中,接下來就由視窗程序處理該執行的工作。
使用者在清單方塊上選擇某些項目或是取消選擇項目等等動作都會觸發清單方塊發送訊息,通知主視窗發生操作者或是清單方塊狀態改變的特定事件,這些訊息都會以"通報訊息" Notification Messages的方式,發送給主視窗的視窗程序。
所謂的"通報訊息"也就是將特定的訊息(已經預先定義好)附加在WM_COMMAND訊息中的wParam參數的高字組,用來進一步區分產生WM_COMMAND訊息的控制項以及觸發的事件。
清單方塊有以下的通報訊息: LBN_DBLCLK 使用者雙擊清單方塊內的項目 LBN_ERRSPACE 清單方塊沒有足夠的記憶體空間 LBN_KILLFOCUS 使用者將輸入焦點轉移出清單方塊 LBN_SELCANCEL 使用者取消選取某個清單方塊的項目 LBN_SELCHANGE 使用者改變所選取的項目 LBN_SETFOCUS 使用者將輸入焦點轉移至清單方塊 現在可以來看看範例程式,如何建立一個清單方塊,然後填入幾個項目,供使用者選擇。
//-------------------start windows_list.cpp---------------
#include < windows.h >
#include < Winuser.h >
const char* szAppName = "MyWndClass";
const int nList_ID = 1; //清單方塊編號
const int nBtn_ID = 2; //關閉視窗按鈕編號
char szText[] = "視窗控制元件-清單方塊";
int nIndex; //清單方塊內項目的索引值
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nTopXY(UINT, UINT);
//程式進入點
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
int wnd_W;
int wnd_H;
wnd_W=400;
wnd_H=300;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szAppName;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc))
{
MessageBox(NULL, "視窗類別登記失敗!", "發生錯誤!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
szAppName,
"視窗控制元件-清單方塊篇",
WS_OVERLAPPEDWINDOW,
nTopXY(wnd_W, GetSystemMetrics(SM_CXSCREEN)),
nTopXY(wnd_H, GetSystemMetrics(SM_CYSCREEN)),
wnd_W,wnd_H,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL)
{
MessageBox(NULL, "視窗建立失敗!", "發生錯誤!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND hList;
HWND hBtn;
HDC hMyDC;
PAINTSTRUCT ps;
int nBX,nBY; //按鈕的座標
int nBW,nBH; //按鈕的寬高
int nLX,nLY; //清單方塊的座標
int nLW,nLH; //清單方塊的寬高
//初始化清單方塊的位置及大小
nLX = 10;
nLY = 10;
nLW = 100;
nLH = 80;
//初始化按鈕的位置及大小
nBX = 10;
nBY = (10+nLH+10);
nBW = 80;
nBH = 30;
//清單項目
char *pItem1="台北";
char *pItem2="台中";
char *pItem3="台南";
char *pItem4="高雄";
//字元陣列
char szBuffer[30];
switch(msg)
{
case WM_CREATE:
//建立清單方塊
hList = CreateWindowEx(
WS_EX_CLIENTEDGE,
TEXT("LISTBOX"),
"清單方塊",
WS_CHILD | LBS_STANDARD,
nLX,
nLY,
nLW,nLH,
hwnd,(HMENU) nList_ID,
((LPCREATESTRUCT) lParam)->hInstance, NULL);
//填四個清單項目
SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem1);
SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem2);
SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem3);
SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem4);
if(hList == NULL) {
MessageBox(NULL, "建立清單方塊失敗!", "發生錯誤!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
//建立關閉視窗按鈕
hBtn = CreateWindowEx(
WS_EX_CLIENTEDGE,
TEXT("BUTTON"),
"關閉視窗",
WS_CHILD | BS_DEFPUSHBUTTON ,
nBX,
nBY,
nBW,nBH,
hwnd,
(HMENU) nBtn_ID,
((LPCREATESTRUCT) lParam)->hInstance,
NULL);
if(hBtn == NULL) {
MessageBox(NULL, "按鈕建立失敗!", "發生錯誤!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
//顯示視窗控制元件
ShowWindow(hList, SW_SHOWNORMAL);
ShowWindow(hBtn, SW_SHOWNORMAL);
break;
case WM_COMMAND:
//過濾觸發WM_COMMAND訊息的控制項
switch(LOWORD(wParam)){
case nList_ID:
//過濾使用者選擇清單項目
if ((HIWORD(wParam)) ==LBN_SELCHANGE){
hList = GetDlgItem(hwnd, nList_ID);
nIndex=SendMessage(hList, LB_GETCURSEL, 0, 0);
SendMessage(hList,LB_GETTEXT,nIndex,(LPARAM) szBuffer);
wsprintf(szText,"選取的是第%d項:%s",nIndex,szBuffer);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case nBtn_ID:
SendMessage(hwnd,WM_CLOSE,0,0);
break;
}
break;
case WM_PAINT:
hMyDC=BeginPaint(hwnd, &ps);
TextOut(hMyDC,100,100,szText,lstrlen(szText));
EndPaint(hwnd, &ps);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
int nTopXY(UINT wnd_XY, UINT wnd_Dim)
{
int resl;
resl=((wnd_Dim/2)-(wnd_XY/2));
return resl;
}
//-----------------end---------------------
//清單項目
char *pItem1="台北";
char *pItem2="台中";
char *pItem3="台南";
char *pItem4="高雄";
因為LB_ADDSTRING訊息的參數需要指向儲存清單項目的字串指標,所以這裡宣告了四個項目的指標,在稍後新建完清單方塊時可以使用。
//字元陣列
char szBuffer[30];
為了取回使用者所選取的清單項目,先宣告一個字元陣列稍後在視窗程序中可以使用。
SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem1); SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem2); SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem3); SendMessage(hList,LB_ADDSTRING,0,(LPARAM) pItem4); 呼叫SendMessage()函式並且加入四個清單項目
case WM_COMMAND:
//過濾觸發WM_COMMAND訊息的控制項
switch(LOWORD(wParam)){
case nList_ID:
//過濾使用者選擇清單項目
if ((HIWORD(wParam)) ==LBN_SELCHANGE){
hList = GetDlgItem(hwnd, nList_ID);
nIndex=SendMessage(hList, LB_GETCURSEL, 0, 0);
SendMessage(hList,LB_GETTEXT,nIndex,(LPARAM) szBuffer);
wsprintf(szText,"選取的是第%d項:%s",nIndex,szBuffer);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
case nBtn_ID:
SendMessage(hwnd,WM_CLOSE,0,0);
break;
}
break;
清單方塊的訊息處理比較迂迴曲折,必須透過幾個不同的步驟才能完成,首先是位在在視窗程序中的WM_COMMAND區塊以LOWORD(wParam)設置條件,過濾觸發WM_COMMAND訊息的視窗控制元件,再以HIWORD(wParam)條件解析存放於WM_COMMAND訊息中的視窗控制項"通報訊息"。
if ((HIWORD(wParam)) ==LBN_SELCHANGE)
使用者在清單方塊上的不同操作都可能觸發WM_COMMAND訊息,所以必需再檢查視窗控制項的"通報訊息",以確定使用者是因為選擇了某個清單選項而觸發WM_COMMAND訊息。
hList = GetDlgItem(hwnd, nList_ID); nIndex=SendMessage(hList, LB_GETCURSEL, 0, 0); SendMessage(hList,LB_GETTEXT,nIndex,(LPARAM) szBuffer); GetDlgItem()函式可以利用視窗控制元件的編號取回元件的視窗代碼,當獲得視窗代碼之後就能以SendMessage()函式發送訊息,要求元件完成特定的工作,以下是GetDlgItem()函式的原型:
HWND GetDlgItem(
HWND hDlg, // 視窗控制元件所屬的主視窗代碼
int nIDDlgItem // 元件編號
);
在取得視窗代碼之後,我們可以呼叫SendMessage()函式將LB_GETCURSEL訊息傳送給清單方塊,就能獲得使用者選取的清單項目索引(0基索引,第一個項目的索引值為0),以下為LB_GETCURSEL訊息的原型。
LB_GETCURSEL
wParam = 0; // 未使用; 必須為0
lParam = 0; // 未使用; 必須為0
呼叫SendMessage()函式將LB_GETTEXT訊息發送給清單方塊,並將儲存清單項目的字元陣列以及剛獲得的索引值一併以參數的形式傳送給清單方塊。
當清單方塊接收到LB_GETTEXT訊息之後,就會將索引值所指定的清單項目(也就是使用者選取的項目)填寫在字元陣列中,並且顯示視窗上,以下為LB_GETTEXT訊息的原型:
LB_GETTEXT wParam = (WPARAM) index;// 要取回文字的項目索引值 lParam = (LPARAM) (LPCTSTR) lpszBuffer; // 字元陣列的指標
case nBtn_ID:
SendMessage(hwnd,WM_CLOSE,0,0);
break;
這個練習重新定義了按鈕的行為,當使用者點擊後會發送WM_CLOSE訊息給主視窗,進而結束視窗應用程式。
"讓物件自己處理本身的問題"
視窗控制元件本身就具備處理訊息以及外觀變化上大部分的工作,程式設計師只要把焦點放在與使用者有關的細節上,這種物件概念的設計方式讓繁瑣的視覺處理變得比較單純,程式在偵錯上或功能的改進上也可以比較容易歸納出問題發生的位置,當元件引用的數量增加,訊息開始熱鬧時,這種設計方式就更顯得簡單而明確。
by Jack 2004/1/14
本文地址:http://com.8s8s.com/it/it32.htm