其實對話框不止這些(子窗口控件)
在書上我們可以看見,About3這個程序,實際上是自繪了一個確認圖標。
首先我們自己給這個圖標定義了一個類(說白了,這個圖標實際上就是一個窗口,只是這裏的窗口類被封裝到了一個叫CustomControl的子窗口控制裏面,這個控件會把Class這一欄空出來由用戶自己選,然後實際上在創建了Dialog的時候就會調用CreateWindow來將這個用戶自定的窗口創建出來。
再說下相互切換的Tab鍵相關的兩個API:
GetNextDlgTabItem和GetNextGroupItem這裏就可以得到前一個或後一個Tab鍵停留的組項(然後設置焦點就可以)同時,如果是自己的窗口的話,要加上WS_TABSTOP和WS_GROUP兩個窗口風格纔可以達到這個目的。
然後是非模態對話框:
1:
第一個是主窗口調用一個非模態對話框(這樣子的對話框更像普通的子窗口一點)
此時我們需要自己爲這個對話框寫一個窗口過程,然後是創建窗口時:
CreateDialog(HINSTANCE,PTCHAR,父窗口窗口句柄 ,自己的窗口過程);
在窗口過程內我們就可以自定義各種各樣的消息,並且可以在主窗口和這個對話框之間相互切換,而且很多的一些相關功能都有着對話框的性質,非常的方便
但是我們會發現一個問題:我們的消息循環只有一個,那要怎麼辦?
此時我們就要用到isDialogMessage這個函數來判斷這個消息是否是屬於後一個對話框的:
if(hWnd == 0||!isDialogMessage(hWnd,&msg))
{
//默認的之前的窗口消息
}
這個函數有兩個作用,第一:如果這個消息時屬於這個對話框的消息,那麼它將返回一個非零值,並且將這個消息發送到相應的窗口過程去,其次:如果這個消息不屬於這個對話框,那麼就返回一個0。
之前加了一個判斷hWnd是否爲0是因爲,如果爲0他將繼續用無效的窗口句柄進行調用這個函數,所以這裏時加以驗證的一個機制,並且,這個hWnd被置爲全局,以便執行:
case WM_CLOSE:
DestoryWindow(hWnd);
hWnd = 0;
return 0;
這裏非模態和模態的區別之一就是這裏並不是調用EndDialog。
2:
我們還可以直接就將這個對話框置爲主窗口,這就會大大簡化了代碼:
WNDCLASS wcex;
wcex.style = CS_HREDRAW | CS_VREDRAW | WS_VISIBLE;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = DLGWINDOWEXTRA; // 這裏必須爲DLGWINDOWEXTRA
wcex.hInstance = hInstance;
...
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
//然後是註冊,之後就用
CreateDialog(HINSTANCE,PTCHAR,0,0);//後面兩個爲NULL就可以了
此時說實話這個就是一個窗口的創建(其尺寸,文字尺寸,標題都在資源文件裏面聲明瞭)兩者時等價關係。
來看一個書上非常典型的例子:
/*----------------------------------------
HEXCALC.C -- Hexadecimal Calculator
(c) Charles Petzold, 1998
----------------------------------------*/
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HexCalc") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = DLGWINDOWEXTRA ; // Note!
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (hInstance, szAppName) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateDialog (hInstance, szAppName, 0, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
void ShowNumber (HWND hwnd, UINT iNumber)
{
TCHAR szBuffer[20] ;
wsprintf (szBuffer, TEXT ("%X"), iNumber) ;
SetDlgItemText (hwnd, VK_ESCAPE, szBuffer) ;
}
DWORD CalcIt (UINT iFirstNum, int iOperation, UINT iNum)
{
switch (iOperation)
{
case '=': return iNum ;
case '+': return iFirstNum + iNum ;
case '-': return iFirstNum - iNum ;
case '*': return iFirstNum * iNum ;
case '&': return iFirstNum & iNum ;
case '|': return iFirstNum | iNum ;
case '^': return iFirstNum ^ iNum ;
case '<': return iFirstNum << iNum ;
case '>': return iFirstNum >> iNum ;
case '/': return iNum ? iFirstNum / iNum: MAXDWORD ;
case '%': return iNum ? iFirstNum % iNum: MAXDWORD ;
default : return 0 ;
}
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static BOOL bNewNumber = TRUE ;
static int iOperation = '=' ;
static UINT iNumber, iFirstNum ;
HWND hButton ;
switch (message)
{
case WM_KEYDOWN: // left arrow --> backspace
if (wParam != VK_LEFT)
break ;
wParam = VK_BACK ;
// fall through
case WM_CHAR:
if ((wParam = (WPARAM) CharUpper ((TCHAR *) wParam)) == VK_RETURN)
wParam = '=' ;
if (hButton = GetDlgItem (hwnd, wParam))
{
SendMessage (hButton, BM_SETSTATE, 1, 0) ;
Sleep (100) ;
SendMessage (hButton, BM_SETSTATE, 0, 0) ;
}
else
{
MessageBeep (0) ;
break ;
}
// fall through
case WM_COMMAND:
SetFocus (hwnd) ;
if (LOWORD (wParam) == VK_BACK) // backspace
ShowNumber (hwnd, iNumber /= 16) ;
else if (LOWORD (wParam) == VK_ESCAPE) // escape
ShowNumber (hwnd, iNumber = 0) ;
else if (isxdigit (LOWORD (wParam))) // hex digit
{
if (bNewNumber)
{
iFirstNum = iNumber ;
iNumber = 0 ;
}
bNewNumber = FALSE ;
if (iNumber <= MAXDWORD >> 4)
ShowNumber (hwnd, iNumber = 16 * iNumber + wParam -
(isdigit (wParam) ? '0': 'A' - 10)) ;
else
MessageBeep (0) ;
}
else // operation
{
if (!bNewNumber)
ShowNumber (hwnd, iNumber =
CalcIt (iFirstNum, iOperation, iNumber)) ;
bNewNumber = TRUE ;
iOperation = LOWORD (wParam) ;
}
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
非常巧妙的是:
這裏將所有的非Char類鍵盤消息全部丟給了默認的窗口過程處理,那麼只需要在控件支持的情況下,我們就可以做到Tab切換等一系列操作,並且藉助於每個消息之間的關係,將撤回鍵(VK_BACK)一直下調到COMMAND,回車鍵轉化等等。。。。
(這個程序我自己在VS2017上實現,無法顯示窗口,目前不知道原因,找到了原因的話會即使更新的)