以下权当学习笔记一篇,初学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 );
}