以下权当学习笔记一篇,初学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----------
下面是源代码:有点罗嗦
001 /********************************************************************
002 created: 2009/04/01
003 created: 1:4:2009 15:27
004 filename: Z:/C++/t8/j.c
005 file path: Z:/C++/t8
006 file base: j
007 file ext: c
008 author: xcntime
009
010 purpose: windows程序设计
011 *********************************************************************/
012 #include <windows.h>
013 #pragma warning(push,4) //发现在Intel C/C++上没用???
014 #pragma comment(linker, "/subsystem:windows /RELEASE ")
015 #pragma message(" 正在进行Windows平台下的GUI程序编译......")
016 #pragma warning(once:177)
017
018 #define MAXRECTS 3
019 #define BLANK 10
020 int iFocus = 0; //子窗口号
021 static int iflag = 0;
022
023 HANDLE hFile;
024 DWORD dwBytesWritten;
025 SYSTEMTIME st;
026 static TCHAR szBuff[255];
027 LRESULT CALLBACK ChildWndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
028 LRESULT CALLBACK WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
029 TCHAR szChildClass[] = TEXT ( "ChildClass" );
030 void fnWritefile(HANDLE hFile,TCHAR szBuff[],DWORD *lpdwBytesWritten)
031 {
032 SetFilePointer ( hFile, 0, NULL, FILE_END ); //没有容错代码
033 WriteFile ( hFile, szBuff, lstrlen ( szBuff ), lpdwBytesWritten, NULL );
034 }
035 int WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
036 {
037 WNDCLASS wndclass;
038 HWND hWnd;
039 MSG msg;
040 static TCHAR szClassName[] = TEXT ( "程序类" );
041
042 RtlZeroMemory ( &wndclass, sizeof ( wndclass ) );
043 wndclass.style = CS_HREDRAW | CS_VREDRAW;
044 wndclass.lpfnWndProc = WndProc;
045 wndclass.lpszClassName = szClassName;
046 wndclass.hInstance = hInstance;
047 wndclass.hIcon = NULL;
048 wndclass.hCursor = LoadCursor ( NULL, IDC_ARROW );
049 wndclass.hbrBackground = CreateSolidBrush ( RGB ( 160, 190, 160 ) ); //(HBRUSH)GetStockObject(WHITE_BRUSH);
050 wndclass.lpszMenuName = NULL;
051 wndclass.cbClsExtra = 0;
052 wndclass.cbWndExtra = 0;
053
054 if ( !RegisterClass ( &wndclass ) )
055 {
056 MessageBox ( NULL, TEXT ( "本程序需要运行在WinNT系列操作系统上!" ), TEXT ( "Error" ), MB_OK | MB_ICONWARNING );
057 }
058
059 wndclass.lpfnWndProc = ChildWndProc;
060 wndclass.lpszClassName = szChildClass;
061 wndclass.hIcon = NULL;
062 wndclass.cbWndExtra = sizeof ( long );
063 RegisterClass ( &wndclass );
064
065 hFile = CreateFile ( "d://MyLog.txt", // create MYFILE.TXT
066 GENERIC_WRITE | GENERIC_READ, // open for writing
067 FILE_SHARE_READ | FILE_SHARE_WRITE, // do not share
068 NULL, // no security
069 OPEN_ALWAYS, // overwrite existing
070 FILE_ATTRIBUTE_NORMAL , // normal file
071
072 NULL ); // no attr. template
073
074 if ( hFile == INVALID_HANDLE_VALUE )
075 {
076 MessageBox ( NULL, TEXT ( "文件创建失败!" ), TEXT ( "警告" ), MB_ICONWARNING ); // process error
077 ExitProcess ( 0 );
078 }
079
080 hWnd = CreateWindow ( szClassName, TEXT ( "我的第N个程序" ), WS_OVERLAPPEDWINDOW,
081 CW_USEDEFAULT,
082 CW_USEDEFAULT,
083 CW_USEDEFAULT,
084 CW_USEDEFAULT,
085 NULL, NULL, hInstance, NULL );
086 ShowWindow ( hWnd, nShowCmd );
087 UpdateWindow ( hWnd );
088
089 while ( GetMessage ( &msg, NULL, 0, 0 ) )
090 {
091 TranslateMessage ( &msg );
092 DispatchMessage ( &msg );
093 }
094
095 return msg.wParam;
096 }
097
098 LRESULT CALLBACK WndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
099 {
100 HDC hDc; //设备描述
101 PAINTSTRUCT ps;
102 static RECT rect; //已改动
103
104 static int cxClient, cyClient, iCurLine = 0 ; //当前窗口宽高
105
106 TEXTMETRIC tm;
107 static int cxChar, cyChar, iMaxWidth, cxCaps; //字体宽高,属性
108 TCHAR szBuffer[256]; //字体缓冲
109
110 int i, j, k, x, y; //常用变量
111
112 static int cxBlock, cyBlock;
113 static int cxyStart, fColor;
114 static HWND hChildWnd[MAXRECTS][MAXRECTS];
115 static BOOL fRemain;
116 POINT pt;
117 switch ( msg )
118 {
119 case WM_CREATE:
120 GetLocalTime ( &st );
121 wsprintf ( szBuff, TEXT ( "---%4d-%2d%2d--%2d:%2d:%2d----------START----------/n序号|窗口对象|/t/t截获的消息|/t/t保存的活动窗口对象|/n" ),
122 st.wYear, st.wMonth, st.wDay,
123 st.wHour, st.wMinute, st.wMinute
124 );
125 fnWritefile(hFile,szBuff,&dwBytesWritten);
126 for ( x = 0;x < MAXRECTS;x++ )
127 {
128 for ( y = 0;y < MAXRECTS;y++ )
129 {
130 wsprintf ( szBuffer, TEXT ( "%d:%d->" ), x, y );
131 hChildWnd[x][y] = CreateWindow ( szChildClass, NULL, WS_CHILDWINDOW | WS_VISIBLE,
132 0, 0, 0, 0, hWnd, ( HMENU ) ( y << 8 | x ),
133 ( HINSTANCE ) GetWindowLong ( hWnd, GWL_HINSTANCE ),
134 NULL );
135 }
136 }
137 hDc = GetDC ( hWnd );
138 GetTextMetrics ( hDc, &tm ); //字体设置
139 cxChar = tm.tmAveCharWidth;
140 cyChar = tm.tmHeight + tm.tmExternalLeading;
141 cxCaps = ( tm.tmPitchAndFamily == 1 ? 1.5 : 1 ) * cxChar;
142 iMaxWidth = cxCaps * 40 + 40 * cxChar;
143 ReleaseDC ( hWnd, hDc );
144 return 0;
145
146 case WM_SIZE:
147 cxClient = LOWORD ( lParam );
148 cyClient = HIWORD ( lParam );
149 cxBlock = cxClient / MAXRECTS - BLANK;
150 cyBlock = cyClient / MAXRECTS - BLANK;
151 cxyStart = ( cxClient - cxBlock * MAXRECTS + cyClient - cyBlock * MAXRECTS ) / 5;
152
153 for ( x = 0;x < MAXRECTS;x++ )
154 {
155 for ( y = 0;y < MAXRECTS;y++ )
156 {
157 MoveWindow ( hChildWnd[x][y], cxyStart + cxBlock *x, cxyStart + cyBlock *y,
158 cxBlock - BLANK, cyBlock - BLANK, TRUE );
159 }
160 }
161 return 0;
162
163 /* case WM_KEYDOWN:
164 SetFocus(GetDlgItem(hWnd,iFocus=y<<8|x));
165 return 0;
166 */
167 case WM_SETFOCUS:
168 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 );
169 fnWritefile(hFile,szBuff,&dwBytesWritten);
170
171 SetFocus ( GetDlgItem ( hWnd, iFocus ) );//注释这行
172 return 0;
173 case WM_KILLFOCUS:
174 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 );
175 fnWritefile(hFile,szBuff,&dwBytesWritten);
176
177 InvalidateRect ( hWnd, NULL, TRUE );
178 return 0;
179 case WM_DESTROY:
180 GetLocalTime ( &st );
181 wsprintf ( szBuff, TEXT ( "---%4d-%2d%2d--%2d:%2d:%2d----------END----------/n/n/n" ), st.wYear, st.wMonth, st.wDay,
182 st.wHour, st.wMinute, st.wMinute );
183 fnWritefile(hFile,szBuff,&dwBytesWritten);
184 CloseHandle ( hFile );
185 PostQuitMessage ( 0 );
186 return 0;
187 }
188
189 return DefWindowProc ( hWnd, msg, wParam, lParam );
190 }
191
192 LRESULT CALLBACK ChildWndProc ( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
193 {
194 HDC hDc; //设备描述
195 PAINTSTRUCT ps;
196 static RECT rect; //已改动
197 static int iCurrentPos;
198
199 iCurrentPos = GetWindowLong ( hWnd, GWL_ID );
200 switch ( msg )
201 {
202 case WM_CREATE:
203 SetWindowLong ( hWnd, 0, 0 );
204 return 0;
205 case WM_LBUTTONDOWN:
206 SetWindowLong ( hWnd, 0, 1 ^ GetWindowLong ( hWnd, 0 ) );
207 wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag,
208 ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_LBUTTONDOWN" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 );
209 fnWritefile(hFile,szBuff,&dwBytesWritten);
210 SetFocus ( hWnd );//注释这行
211 InvalidateRect ( hWnd, NULL, FALSE );
212 return 0;
213 case WM_PAINT:
214 hDc = BeginPaint ( hWnd, &ps );
215 GetClientRect ( hWnd, &rect );
216 Rectangle ( hDc, BLANK, BLANK, rect.right - BLANK, rect.bottom - BLANK );
217 TextOut ( hDc, rect.right / 3, rect.bottom / 2, szBuff,
218 wsprintf ( szBuff, TEXT ( "Child:(%d,%d)" ), ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1 )
219 );
220 if ( GetWindowLong ( hWnd, 0 ) )
221 {
222 MoveToEx ( hDc, BLANK, BLANK, NULL );
223 LineTo ( hDc, rect.right - BLANK, rect.bottom - BLANK );
224 MoveToEx ( hDc, BLANK, rect.bottom - BLANK, NULL );
225 LineTo ( hDc, rect.right - BLANK, BLANK );
226 }
227 EndPaint ( hWnd, &ps );
228 return 0;
229 case WM_KEYDOWN:
230 wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag,
231 ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_KEYDOWN" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 );
232 fnWritefile(hFile,szBuff,&dwBytesWritten);
233 return 0;
234 case WM_SETFOCUS:
235 iFocus = GetWindowLong ( hWnd, GWL_ID );
236 wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag,
237 ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_SETFOCUS" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 );
238 fnWritefile(hFile,szBuff,&dwBytesWritten);
239 return 0;
240 case WM_KILLFOCUS:
241 wsprintf ( szBuff, TEXT ( "%3d/t:Child:(%d,%d)/t/t%-15s/t/tWindow:(%d.%d)/n" ), ++iflag,
242 ( iCurrentPos >> 8 ) + 1, ( iCurrentPos&0xFF ) + 1, TEXT ( "WM_KILLFOCUS" ), ( iFocus >> 8 ) + 1, ( iFocus&0xFF ) + 1 );
243 fnWritefile(hFile,szBuff,&dwBytesWritten);
244 InvalidateRect ( hWnd, NULL, TRUE );
245 return 0;
246 }
247 return DefWindowProc ( hWnd, msg, wParam, lParam );
248 }
249
250