对窗口创建的详细解释
============
·窗口的注册
一个窗口的风格, 总是要根据窗口类来决定的, 同时, 窗口类决定了处理窗口消息所使用的函数(窗口过程)。 当定义好窗口类对象并完成对象属性后, 如果想要建立这个窗口必须调用函数RegisterClass来注册窗口类, RegisterClass的函数原型如下:
ATOM RegisterClass(CONST WNDCLASS *lpWndClass);
该函数只有一个参数, 一个指向一个WNDCLASS结构的指针, 现在, 我们再来看一下WNDCLASS窗口类的结构, 下面是等效定义, 和WINUSER.H中的WNDCLASS效果相同, 但WINUSER.H中是通过定义tagWNDCLASSA(ASCII版本)和tagWNDCLASSW(Unicode版本), 然后进行 :
#ifdef UNICODE typedef WNDCLASSW WNDCLASS; typedef PWNDCLASSW PWNDCLASS; typedef NPWNDCLASSW NPWNDCLASS; typedef LPWNDCLASSW LPWNDCLASS; #else typedef WNDCLASSA WNDCLASS; typedef PWNDCLASSA PWNDCLASS; typedef NPWNDCLASSA NPWNDCLASS; typedef LPWNDCLASSA LPWNDCLASS; #endif // UNICODE
来实现 WNDCLASS的定义, 在效果上和以下定义是相同的:
typedef struct tagWNDCLASS { UINT style; //窗口样式 WNDPROC lpfnWndProc; //回调函数 int cbClsExtra; //窗口类扩展 int cbWndExtra; //窗口实例扩展 HINSTANCE hInstance; //窗口实例句柄 HICON hIcon; //窗口最小化图标 HCURSOR hCursor; //窗口采用鼠标指针样式 HBRUSH hbrBackground; //窗口背景颜色 LPCWSTR lpszMenuName; //窗口菜单 LPCWSTR lpszClassName; //窗口类名 } WNDCLASS, *PWNDCLASS;
在WinMain主函数中, 我们使用WNDCLASS创建了一个窗口类对象:wndclass, 并且对wndclass的属性做了如下设置:
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口样式 wndclass.lpszClassName = szAppName ; //窗口类名 wndclass.lpszMenuName = NULL ; //窗口菜单:无 wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ; //窗口背景颜色 wndclass.lpfnWndProc = WndProc ; //窗口消息处理函数 wndclass.cbWndExtra = 0 ; //窗口实例扩展:无 wndclass.cbClsExtra = 0 ; //窗口类扩展:无 wndclass.hInstance = hInstance ; //窗口实例句柄 wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; //窗口最小化图标:使用缺省图标 wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; //窗口采用箭头光标
下面我们来详细看下WNDCLASS的这些成员:
1>. wndclass.style = CS_HREDRAW | CS_VREDRAW ;
style属性决定着窗口的风格, 可以通过C语言的或运算组合出不同风格类型的窗口, 在WINUSER.H头文件中定义着这些标识符以及其值, 所有的前缀为CS_的标识符如下:
/* * Class styles */ #define CS_VREDRAW 0x0001 #define CS_HREDRAW 0x0002 #define CS_DBLCLKS 0x0008 #define CS_OWNDC 0x0020 #define CS_CLASSDC 0x0040 #define CS_PARENTDC 0x0080 #define CS_NOCLOSE 0x0200 #define CS_SAVEBITS 0x0800 #define CS_BYTEALIGNCLIENT 0x1000 #define CS_BYTEALIGNWINDOW 0x2000 #define CS_GLOBALCLASS 0x4000 #define CS_IME 0x00010000
2>. wndclass.lpszClassName = szAppName ;
赋予窗口一个名称, 可以使用ASCII版本的字符串也可以使用Unicode版本的字符串, 需要注意的是, 这里的窗口名称并不是指窗口标题。
3>. wndclass.lpszMenuName = NULL ;
指定窗口类的菜单, 由于我们这个窗口没有使用菜单, 所以为NULL, 具体的用法以后肯定会学习到;
4>. wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ;
窗口的背景颜色, 这里使用的是以白色作为填充, 填充白色这是常见的做法, 如果你愿意, 也可以填充为其他的一些样式, 这里是一些样式的标识符:
#define WHITE_BRUSH 0 #define LTGRAY_BRUSH 1 #define GRAY_BRUSH 2 #define DKGRAY_BRUSH 3 #define BLACK_BRUSH 4 #define NULL_BRUSH 5 #define HOLLOW_BRUSH NULL_BRUSH #define WHITE_PEN 6 #define BLACK_PEN 7 #define NULL_PEN 8 #define OEM_FIXED_FONT 10 #define ANSI_FIXED_FONT 11 #define ANSI_VAR_FONT 12 #define SYSTEM_FONT 13 #define DEVICE_DEFAULT_FONT 14 #define DEFAULT_PALETTE 15 #define SYSTEM_FIXED_FONT 16
5>. wndclass.lpfnWndProc = WndProc ;
将窗口的消息处理函数设置为我们自定义的WndProc函数。
6>. wndclass.cbWndExtra = 0 ;
7>. wndclass.cbClsExtra = 0 ;
这两个属性用来维护结构中预留的一些额外空间, 程序可以根据需要要使用这些额外空间, 通过匈牙利命名的cbClsExtra、cbWndExtra中的cb可以知道, 这些成员表示一个"字节数", 由于我们这个窗口没有使用到额外空间, 所以将这个两个属性赋值为0;
8>. wndclass.hInstance = hInstance ;
表示应用程序的实例句柄, hInstance的来源是WinMain的一个参数;
9>. wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ;
加载一个位图作为程序标题栏最左侧的小图标, 我们这里加载了一个系统的默认图标, 后面的学习这将会学习到如何从磁盘加载我们自定义的图标.
10>. wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ;
与LoadIcon类似, 加载鼠标的指针位图;
完成这是个窗口类成员属性的初始化后, 我们就可以通过RegisterClass来注册窗口了, 如果注册成功, 将返回一个具有唯一标识已注册的类的一个原子, 如果函数失败,返回0。
·窗口的创建
成功注册窗口类后, 我们就可以呼叫CreateWindow函数对窗口进行创建了, CreateWindow函数的原型如下:
HWND CreateWindow( LPCTSTR lpClassName, //窗口类名称 LPCTSTR lpWindowName, //窗口标题 DWORD dwStyle, //窗口样式 int x, //窗口初始x坐标 int y, //窗口初始y坐标 int nWidth, //窗口初始x方向尺寸 int nHeight, //窗口初始y方向尺寸 HWND hWndParent, //父窗口句柄 HMENU hMenu, //窗口菜单句柄 HANDLE hlnstance, //程序实例句柄 LPVOID lpParam //创建参数 );
函数返回的是创建成功后的窗口的句柄。 需要说明的几点:
1>.参数一: LPCTSTR lpClassName
参数LPCTSTR lpClassName,为窗口的名称, 我们需要传入的参数就是刚才我们在窗口类注册的窗口类名称, 这样使用CreateWindow建立的窗口就能与注册的窗口进行关联。
2>. 参数三: DWORD dwStyle
参数三为窗口的样式, 示例中创建的是一个普通的层叠窗口样式, WS_OVERLAPPEDWINDOW, 打开WINUSER.H头文件, 对于WS_OVERLAPPEDWINDOW是这样定义的:
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION | \ WS_SYSMENU | \ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX)
可以看出, WS_OVERLAPPEDWINDOW样式实际上是通过一些标识符通过或的组合, 此外我们还可以通过组合设计我们自己的样式, 这些标识符在WINUSER.H的定义如下:
#define WS_OVERLAPPED 0x00000000L //产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同。 #define WS_POPUP 0x80000000L //创建一个弹出式窗口。该风格不能与WS_CHLD风格同时使用。 #define WS_CHILD 0x40000000L //创建一个子窗口。这个风格不能与WS_POPUP风格合用。 #define WS_MINIMIZE 0x20000000L //创建一个初始状态为最小化的窗口。仅与WS_OVERLAPPED风格一起使用。 #define WS_VISIBLE 0x10000000L //创建一个最初可见的窗口。 #define WS_DISABLED 0x08000000L //创建一个初始状态为禁止的窗口。 #define WS_CLIPSIBLINGS 0x04000000L //排除子窗口之间的相对区域。 #define WS_CLIPCHILDREN 0x02000000L //创建一个初始状态为禁止的子窗口。一个禁止状态的窗口不能接受来自用户的输入信息。 #define WS_MAXIMIZE 0x01000000L //创建一个初始状态为最大化状态的窗口。 #define WS_CAPTION 0x00C00000L //创建一个有标题框的窗口(包括WS_BODER风格)。 #define WS_BORDER 0x00800000L //创建一个单边框的窗口。 #define WS_DLGFRAME 0x00400000L //创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条。 #define WS_VSCROLL 0x00200000L //创建一个有垂直滚动条的窗口。 #define WS_HSCROLL 0x00100000L //创建一个有水平滚动条的窗口。 #define WS_SYSMENU 0x00080000L //创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格。 #define WS_THICKFRAME 0x00040000L //创建一个具有可调边框的窗口,与WS_SIZEBOX风格相同。 #define WS_GROUP 0x00020000L //指定一组控制的第一个控制。 #define WS_TABSTOP 0x00010000L //创建一个控制,这个控制在用户按下Tab键时可以获得键盘焦点。按下Tab键后使键盘焦点转移到下一具有WS_TABSTOP风格的控制。 #define WS_MINIMIZEBOX 0x00020000L //创建一个具有最小化按钮的窗口。 #define WS_MAXIMIZEBOX 0x00010000L //创建一个具有最大化按钮的窗口。 #define WS_TILED WS_OVERLAPPED //产生一个层叠的窗口。一个层叠的窗口有一个标题和一个边框。与WS_OVERLAPPED风格相同。 #define WS_ICONIC WS_MINIMIZE //创建一个初始状态为最小化的窗口。仅与WS_OVERLAPPED风格一起使用。 #define WS_SIZEBOX WS_THICKFRAME //创建一个具有厚边框的窗口,可以通过厚边框来改变窗口大小。 #define WS_TILEDWINDOW WS_OVERLAPPEDWINDOW //创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU,WS_THICKFRAME,WS_MINIMIZEBOX和WS_MAXIMIZEBOX风格的重叠式窗口。
关于这些样式的详细说明可以查阅MSDN Library(推荐)或到百科查询。
·窗口的显示
使用CreateWindow函数成功创建好窗口后, 这个窗口就被在保存在一段内存中了, 但是此时窗口还不能显示在屏幕上, 我们需要使用ShowWindow函数来将建立好的窗口显示出来, ShowWindow函数的原型如下:
BOOL ShowWindow( HWND hWnd, int iCmdShow );
参数一为刚才通过CreateWindow函数窗口出的窗口句柄, 参数二为窗口的显示方式, 由WinMain函数的int iCmdShow接收该值, 常用的显示方式有:
SW_HIDE //隐藏窗口并激活其他窗口; SW_MAXIMIZE //最大化; SW_MINIMIZE //最小化指定的窗口并且激活在Z序中的下一个顶层窗口; SW_RESTORE //激活并显示窗口。如果窗口最小化或最大化,则系统将窗口恢复到原来的尺寸和位置; SW_SHOW //在窗口原来的位置以原来的尺寸激活和显示窗口; SW_SHOWMAXIMIZED //激活窗口并将其最大化; SW_SHOWMINIMIZED //激活窗口并将其最小化; SW_SHOWMINNOACTIVE //窗口最小化,激活窗口仍然维持激活状态;
SW_SHOWNA //以窗口原来的状态显示窗口。激活窗口仍然维持激活状态; SW_SHOWNOACTIVATE //以窗口最近一次的大小和状态显示窗口。激活窗口仍然维持激活状态; SW_SHOWNORMAL //激活并显示一个窗口。如果窗口被最小化或最大化,系统将其恢复到原来的尺寸和大小;
·窗口的更新
当窗口客户区需要进行更新时, UpdateWindow函数就会被调用, 并绕过消息队列直接发送一个WM_PAINT的消息给窗口过程函数WndProc进行相应处理, UpdateWindow函数的原型如下:
BOOL UpdateWindow( HWND hWnd );
该函数只有一个参数, 参数为需要更新的窗口句柄; 函数调用成功返回值为非0, 调用失败时返回值为0。
明天学习窗口过程WndProc是如何处理这些消息的。
--------------------
Wid, 2012.10.09
上一篇: C语言Windows程序设计->第四天->详解我的窗口(上)