其实对话框不止这些(子窗口控件)
在书上我们可以看见,About3这个程序,实际上是自绘了一个确认图标。
首先我们自己给这个图标定义了一个类(说白了,这个图标实际上就是一个窗口,只是这里的窗口类被封装到了一个叫CustomControl的子窗口控制里面,这个控件会把Class这一栏空出来由用户自己选,然后实际上在创建了Dialog的时候就会调用CreateWindow来将这个用户自定的窗口创建出来。
再说下相互切换的Tab键相关的两个API:
GetNextDlgTabItem和GetNextGroupItem这里就可以得到前一个或后一个Tab键停留的组项(然后设置焦点就可以)同时,如果是自己的窗口的话,要加上WS_TABSTOP和WS_GROUP两个窗口风格才可以达到这个目的。
然后是非模态对话框:
第一个是主窗口调用一个非模态对话框(这样子的对话框更像普通的子窗口一点)
此时我们需要自己为这个对话框写一个窗口过程,然后是创建窗口时:
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。
我们还可以直接就将这个对话框置为主窗口,这就会大大简化了代码:
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
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上实现,无法显示窗口,目前不知道原因,找到了原因的话会即使更新的)