以下权当学习笔记一篇,初学Win32编程的一个小坎,:父子窗口的输入焦点问题,子窗口无法自动获得输入焦点?
程序窗口的输入焦点用于表示哪个窗口有资格接收键盘输入消息。带有输入焦点的窗口或是一个活动窗口,或者是该活动窗口的子窗口。
当一个顶层窗口获得输入焦点时,Windows向该窗口发送WM_SETFOCUS消息,此窗口可将输入焦点重定位到它的子窗口上。子窗口不会自动获得输入焦点。失去输入焦点的窗口会收到WM_KILLFOCUS消息。当子窗口拥有输入焦点时,父窗口就不会处理键盘输入了。
用户使用按钮时.按钮获得输入焦点而其父窗口失去输入焦点.这时父窗口先收到WM_KILLFOCUS消息(wParam参数为获得输入焦点的窗口的句柄).然后获得输入焦点的窗口(按钮子窗口)收到一个WM_SETFOCUS消息(wParam参数为失去输入焦点的窗口的句柄).
所谓“子窗口不会自动获得输入焦点”的具体含义是:
1. 若一程序a父窗口A上有子窗口B,C,D,此时从另一程序b直接(不是先点击A,再点击B)用鼠标(例如:WM_LBUTTONDOWN)切换到a程序的B窗口上,则a程序接受消息顺序是: 父窗口A响应收到的WM_SETFOCUS消息,继而子窗口B响应收到的WM_LBUTTONDOWN消息,一般这两个响应过程里都可以添加SetFocus( )函数以确定当前子窗口B具有输入焦点,一般添加SetFocus( B)使B获得焦点,当然Coder不添加SetFocus( B)则B无法收到键盘消息.
2. 但是!?.若从程序a的子窗口B直接切换到子窗口C上,则a程序接受消息顺序是:子窗口B响应收到的WM_LBUTTONDOWN,继而B窗口响应WM_KILLFOCUS,再C响应WM_SETFOCUS.这个情况大家们似乎不屑于说,或者不值得提.
Charles Petzold所著Programming Windows一书中第七章的7-5 CHECKER4.C(Mouse Hit-Test Demo Program No. 4)程序中一书中有以下说明:
“在CHECKER4中,整体变量idFocus用于保存目前输入焦点窗口的子窗口ID。我在前面说过,当您在子窗口上面单击鼠标时,它们不会自动获得输入焦点。因此,CHECKER4中的父窗口将通过呼叫下面的函数来处理WM_SETFOCUS消息:
SetFocus (GetDlgItem (hwnd, idFocus)) ;这样设定一个子窗口为输入焦点。
ChildWndProc处理WM_SETFOCUS和WM_KILLFOCUS消息。对于WM_SETFOCUS,它将保存在整体变量idFocus中接收输入焦点的子窗口ID。……
……在风格相似的CHECKER2中,此程序可获得有输入焦点的子窗口的x和y坐标,并根据按下的特定方向键来改变它们。然后通过呼叫SetFocus将输入焦点设定给新的子窗口。”
上述基本肯定了我的看法.
下面是一个验证程序,通过注释掉回调函数的SetFocus()观察其用法,仅当学习了哈,验证途径:打开画图程序(随便一个其他Windows程序),运行编译后的程序j.exe,左键单击子窗口(1,1),(1,2),键盘输入字母a,切换到画图程序,再切换到j.exe的标题栏,键盘输入字母b,关闭j.exe,查看d://MyLog.txt内容,顺序不要错了哈,
---2009- 4 5--21:50:50----------START---------- //保留父子函数体中的SetFocus();
序号|窗口对象| 截获的消息| 保存的活动窗口对象|
1 :Parent: WM_SETFOCUS Window:(0.256)//总是父窗口先收到哈
2 :Parent: WM_KILLFOCUS Window:(0.256)// 父窗口函数SetFocus引起,因(0,256)无效,接下来不存在Window:(0.256) WM_SETFOCUS,把iFocus初始值改为0就是另外结果.
3 :Child:(2,1) WM_LBUTTONDOWN Window:(0.256)// 单击子窗口(1,1),
4 :Child:(2,1) WM_SETFOCUS Window:(2.1) // 子窗口函数中的SetFocus引起
5 :Child:(2,2) WM_LBUTTONDOWN Window:(2.1)// 单击子窗口(1,2)
6 :Child:(2,1) WM_KILLFOCUS Window:(2.1)
7 :Child:(2,2) WM_SETFOCUS Window:(2.2) // 子窗口函数中的SetFocus引起
8 :Child:(2,2) WM_KEYDOWN Window:(2.2)// 输入字母a
9 :Child:(2,2) WM_KILLFOCUS Window:(2.2)
10 :Parent: WM_SETFOCUS Window:(2.2)
11 :Parent: WM_KILLFOCUS Window:(2.2) // 父窗口函数中的SetFocus引起
12 :Child:(2,2) WM_SETFOCUS Window:(2.2) // 父窗口函数中的SetFocus引起
13 :Child:(2,2) WM_KEYDOWN Window:(2.2)// 在保存的活动窗口输入字母b,
14 :Child:(2,2) WM_KILLFOCUS Window:(2.2)//失去焦点,退出程序
---2009- 4 5--21:50:50----------END----------
---2009- 4 5--21:53:53----------START---------- //仅留子函数体中的SetFocus();
序号|窗口对象| 截获的消息| 保存的活动窗口对象|
1 :Parent: WM_SETFOCUS Window:(0.256)
2 :Child:(2,1) WM_LBUTTONDOWN Window:(0.256) // 单击子窗口(1,1)
3 :Parent: WM_KILLFOCUS Window:(0.256)
4 :Child:(2,1) WM_SETFOCUS Window:(2.1)
5 :Child:(2,2) WM_LBUTTONDOWN Window:(2.1) // 单击子窗口(1,2)
6 :Child:(2,1) WM_KILLFOCUS Window:(2.1)
7 :Child:(2,2) WM_SETFOCUS Window:(2.2)
8 :Child:(2,2) WM_KEYDOWN Window:(2.2) //输入字母a
9 :Child:(2,2) WM_KEYDOWN Window:(2.2)// 输入字母b
10 :Child:(2,2) WM_KILLFOCUS Window:(2.2)
11 :Parent: WM_SETFOCUS Window:(2.2)
12 :Parent: WM_KILLFOCUS Window:(2.2)
---2009- 4 5--21:53:53----------END----------
---2009- 4 5--21:54:54----------START---------- //仅留父函数体中的SetFocus();
序号|窗口对象| 截获的消息| 保存的活动窗口对象|
1 :Parent: WM_SETFOCUS Window:(0.256)
2 :Parent: WM_KILLFOCUS Window:(0.256) // 父窗口函数中的SetFocus引起
3 :Child:(2,1) WM_LBUTTONDOWN Window:(0.256) // 单击子窗口(1,1)
4 :Child:(2,2) WM_LBUTTONDOWN Window:(0.256) // 单击子窗口(1,2)
5 :Parent: WM_SETFOCUS Window:(0.256)//子窗口无法响应键盘输入
6 :Parent: WM_KILLFOCUS Window:(0.256)
---2009- 4 5--21:54:54----------END----------
下面是源代码:有点罗嗦 /******************************************************************** created: 2009/04/01 created: 1:4:2009 15:27 filename: Z:/C++/t8/j.c file path: Z:/C++/t8 file base: j file ext: c author: xcntime purpose: windows程序设计 *********************************************************************/ #include <windows.h> #pragma warning(push,4) //发现在Intel C/C++上没用??? #pragma comment(linker, "/subsystem:windows /RELEASE ") #pragma message(" 正在进行Windows平台下的GUI程序编译......") #pragma warning(once:177) #define MAXRECTS 3 #define BLANK 10 int iFocus = 0; //子窗口号 static int iflag = 0; HANDLE hFile; DWORD dwBytesWritten; SYSTEMTIME st; static TCHAR szBuff[255]; LRESULT CALLBACK ChildWndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); LRESULT CALLBACK WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ); TCHAR szChildClass[] = TEXT ( "ChildClass" ); void fnWritefile(HANDLE hFile,TCHAR szBuff[],DWORD *lpdwBytesWritten) { SetFilePointer ( hFile, 0, NULL, FILE_END ); //没有容错代码 WriteFile ( hFile, szBuff, lstrlen ( szBuff ), lpdwBytesWritten, NULL ); } int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { WNDCLASS wndclass; HWND hWnd; MSG msg; static TCHAR szClassName[] = TEXT ( "程序类" ); RtlZeroMemory ( &wndclass, sizeof ( wndclass ) ); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.lpszClassName = szClassName; wndclass.hInstance = hInstance; wndclass.hIcon = NULL; wndclass.hCursor = LoadCursor ( NULL, IDC_ARROW ); wndclass.hbrBackground = CreateSolidBrush ( RGB ( 160, 190, 160 ) ); //(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; if ( !RegisterClass ( &wndclass ) ) { MessageBox ( NULL, TEXT ( "本程序需要运行在WinNT系列操作系统上!" ), TEXT ( "Error" ), MB_OK | MB_ICONWARNING ); } wndclass.lpfnWndProc = ChildWndProc; wndclass.lpszClassName = szChildClass; wndclass.hIcon = NULL; wndclass.cbWndExtra = sizeof ( long ); RegisterClass ( &wndclass ); hFile = CreateFile ( "d://MyLog.txt", // create MYFILE.TXT GENERIC_WRITE | GENERIC_READ, // open for writing FILE_SHARE_READ | FILE_SHARE_WRITE, // do not share NULL, // no security OPEN_ALWAYS, // overwrite existing FILE_ATTRIBUTE_NORMAL , // normal file NULL ); // no attr. template if ( hFile == INVALID_HANDLE_VALUE ) { MessageBox ( NULL, TEXT ( "文件创建失败!" ), TEXT ( "警告" ), MB_ICONWARNING ); // process error ExitProcess ( 0 ); } hWnd = CreateWindow ( szClassName, TEXT ( "我的第N个程序" ), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow ( hWnd, nShowCmd ); UpdateWindow ( hWnd ); while ( GetMessage ( &msg, NULL, 0, 0 ) ) { TranslateMessage ( &msg ); DispatchMessage ( &msg ); } return msg.wParam; } LRESULT CALLBACK WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { HDC hDc; //设备描述 PAINTSTRUCT ps; static RECT rect; //已改动 static int cxClient, cyClient, iCurLine = 0 ; //当前窗口宽高 TEXTMETRIC tm; static int cxChar, cyChar, iMaxWidth, cxCaps; //字体宽高,属性 TCHAR szBuffer[256]; //字体缓冲 int i, j, k, x, y; //常用变量 static int cxBlock, cyBlock; static int cxyStart, fColor; static HWND hChildWnd[MAXRECTS][MAXRECTS]; static BOOL fRemain; POINT pt; switch ( msg ) { case WM_CREATE: GetLocalTime ( &st ); wsprintf ( szBuff, TEXT ( "---%4d-%2d%2d--%2d:%2d:%2d----------START----------/n序号|窗口对象|/t/t截获的消息|/t/t保存的活动窗口对象|/n" ), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wMinute ); fnWritefile(hFile,szBuff,&dwBytesWritten); for ( x = 0;x < MAXRECTS;x++ ) { for ( y = 0;y < MAXRECTS;y++ ) { wsprintf ( szBuffer, TEXT ( "%d:%d->" ), x, y ); hChildWnd[x][y] = CreateWindow ( szChildClass, NULL, WS_CHILDWINDOW | WS_VISIBLE, 0, 0, 0, 0, hWnd, ( HMENU ) ( y << 8 | x ), ( HINSTANCE ) GetWindowLong ( hWnd, GWL_HINSTANCE ), NULL ); } } hDc = GetDC ( hWnd ); GetTextMetrics ( hDc, &tm ); //字体设置 cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight + tm.tmExternalLeading; cxCaps = ( tm.tmPitchAndFamily == 1 ? 1.5 : 1 ) * cxChar; iMaxWidth = cxCaps * 40 + 40 * cxChar; ReleaseDC ( hWnd, hDc ); return 0; case WM_SIZE: cxClient = LOWORD ( lParam ); cyClient = HIWORD ( lParam ); cxBlock = cxClient / MAXRECTS - BLANK; cyBlock = cyClient / MAXRECTS - BLANK; cxyStart = ( cxClient - cxBlock * MAXRECTS + cyClient - cyBlock * MAXRECTS ) / 5; for ( x = 0;x < MAXRECTS;x++ ) { for ( y = 0;y < MAXRECTS;y++ ) { MoveWindow ( hChildWnd[x][y], cxyStart + cxBlock *x, cxyStart + cyBlock *y, cxBlock - BLANK, cyBlock - BLANK, TRUE ); } } return 0; /* case WM_KEYDOWN: SetFocus(GetDlgItem(hWnd,iFocus=y<<8|x)); return 0; */ case WM_SETFOCUS: wsprintf ( szBuff, TEXT ( "%3d/t:Parent:/t/t%-15s/t/t/tWindow:(%d.%d)/n" ), ++iflag, TEXT ( "WM_SETFOCUS" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 ); fnWritefile(hFile,szBuff,&dwBytesWritten); SetFocus ( GetDlgItem ( hWnd, iFocus ) );//注释这行 return 0; case WM_KILLFOCUS: wsprintf ( szBuff, TEXT ( "%3d/t:Parent:/t/t%-15s/t/t/tWindow:(%d.%d)/n" ), ++iflag, TEXT ( "WM_KILLFOCUS" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 ); fnWritefile(hFile,szBuff,&dwBytesWritten); InvalidateRect ( hWnd, NULL, TRUE ); return 0; case WM_DESTROY: GetLocalTime ( &st ); wsprintf ( szBuff, TEXT ( "---%4d-%2d%2d--%2d:%2d:%2d----------END----------/n/n/n" ), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wMinute ); fnWritefile(hFile,szBuff,&dwBytesWritten); CloseHandle ( hFile ); PostQuitMessage ( 0 ); return 0; } return DefWindowProc ( hWnd, msg, wParam, lParam ); } LRESULT CALLBACK ChildWndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { HDC hDc; //设备描述 PAINTSTRUCT ps; static RECT rect; //已改动 static int iCurrentPos; iCurrentPos = GetWindowLong ( hWnd, GWL_ID ); switch ( msg ) { case WM_CREATE: SetWindowLong ( hWnd, 0, 0 ); return 0; case WM_LBUTTONDOWN: SetWindowLong ( hWnd, 0, 1 ^ GetWindowLong ( hWnd, 0 ) ); wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag, ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_LBUTTONDOWN" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 ); fnWritefile(hFile,szBuff,&dwBytesWritten); SetFocus ( hWnd );//注释这行 InvalidateRect ( hWnd, NULL, FALSE ); return 0; case WM_PAINT: hDc = BeginPaint ( hWnd, &ps ); GetClientRect ( hWnd, &rect ); Rectangle ( hDc, BLANK, BLANK, rect.right - BLANK, rect.bottom - BLANK ); TextOut ( hDc, rect.right / 3, rect.bottom / 2, szBuff, wsprintf ( szBuff, TEXT ( "Child:(%d,%d)" ), ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1 ) ); if ( GetWindowLong ( hWnd, 0 ) ) { MoveToEx ( hDc, BLANK, BLANK, NULL ); LineTo ( hDc, rect.right - BLANK, rect.bottom - BLANK ); MoveToEx ( hDc, BLANK, rect.bottom - BLANK, NULL ); LineTo ( hDc, rect.right - BLANK, BLANK ); } EndPaint ( hWnd, &ps ); return 0; case WM_KEYDOWN: wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag, ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_KEYDOWN" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 ); fnWritefile(hFile,szBuff,&dwBytesWritten); return 0; case WM_SETFOCUS: iFocus = GetWindowLong ( hWnd, GWL_ID ); wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag, ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_SETFOCUS" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 ); fnWritefile(hFile,szBuff,&dwBytesWritten); return 0; case WM_KILLFOCUS: wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag, ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_KILLFOCUS" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 ); fnWritefile(hFile,szBuff,&dwBytesWritten); InvalidateRect ( hWnd, NULL, TRUE ); return 0; } return DefWindowProc ( hWnd, msg, wParam, lParam ); }