Windows编程中,对所有的GUI组件和控件进行了分类,每种类型的实例对象都具有不同的特性,这些特性包括UI外观以及事件的处理和响应的方法。就和面向对象技术中的类和实例对象一样,Windows中也提供了窗口类和窗口实例的概念,在创建一个窗口对象是必须要指定对应的窗口类名称,所有的窗口类必须要先注册到系统中才能进行实例化创建。系统内部默认注册了一些窗口类,比如按钮,编辑框这些窗口类等等。本文所要介绍的就是那些针对窗口类进行操作的API。一个窗口类其实就是定义了这种窗口实例的外观显示的样式、光标在窗口上移动时的样式、以及图标样式、背景绘制的画刷的类型、菜单、以及对应的UI事件处理函数等等。为了唯一的表征一个窗口类,还需要为窗口类指定一个唯一的字符串名称。下面的结构体就是用来描述一个窗口类所应该具有的数据结构:
typedef struct _WNDCLASS {
UINT style; //窗口显示的样式
WNDPROC lpfnWndProc; //窗口事件处理函数
int cbClsExtra; //窗口类的附加数据尺寸
int cbWndExtra; //窗口实例对象的附加数据尺寸
HINSTANCE hInstance; //窗口类所属的应用句柄
HICON hIcon; //窗口的图标
HCURSOR hCursor; //窗口的光标
HBRUSH hbrBackground; //窗口的背景刷
LPCTSTR lpszMenuName; //窗口的菜单
LPCTSTR lpszClassName; //窗口的类名字符串
} WNDCLASS, *PWNDCLASS;
你还可以使用WNDCLASSEX结构来指定一个窗口类更多的信息。
- 窗口类的注册
在建立一个窗口类的窗口实例对象前,要先注册这个窗口类,这可以通过调用如下函数来完成。
ATOM RegisterClass(
CONST WNDCLASS *lpWndClass // class data
);
ATOM RegisterClassEx(
CONST WNDCLASSEX *lpwcx // class data
);
函数的参数就是执行的窗口类数据结构WNDCLASS或者WNDCLASSEX。这两个函数的返回一个原子标识值,用来表示注册的这个窗口类的唯一标识值。
- 窗口类的反注册
如果不再需要某个窗口类了,就可以调用反注册函数:
BOOL UnregisterClass(
LPCTSTR lpClassName, // 窗口类的类名
HINSTANCE hInstance // 应用程序句柄
);
- 窗口类信息的获取
你也可以在窗口类被注册完成后通过如下的函数来进行窗口类信息的获取:
BOOL GetClassInfo(
HINSTANCE hInstance, //[IN]应用程序句柄通常为NULL
LPCTSTR lpClassName, //[IN]窗口类的类名
LPWNDCLASS lpWndClass //[OUT]窗口类的信息为WNDCLASS结构
);
BOOL GetClassInfoEx(
HINSTANCE hinst, // handle to application instance
LPCTSTR lpszClass, // class name
LPWNDCLASSEX lpwcx // class data
);
- 窗口的创建
当注册完一个窗口类后,就可以使用窗口类对应的ClassName来创建这个窗口类下的窗口实例了。
HWND CreateWindow(
LPCTSTR lpClassName, //RegisterClass方法注册的窗口类或者系统默认的窗口类字符串。
LPCTSTR lpWindowName, //窗口名称
DWORD dwStyle, //窗口的样式
int x, //位置
int y,
int nWidth, //尺寸
int nHeight,
HWND hWndParent, //所属的父窗口
HMENU hMenu, //关联的菜单
HANDLE hlnstance, //应用程序句柄
LPVOID lpParam //附加参数。
)
- 从窗口中获取所属的窗口类的名称
int GetClassName(
HWND hWnd, //[IN]窗口句柄
LPTSTR lpClassName, //[OUT]窗口的类名
int nMaxCount //[IN]类名的长度
);
- 从窗口中获取所属的窗口类的信息
我们可以通过GetClassName来获取一个窗口所属的窗口类的类名,还可以根据窗口类的类名通过GetClassInfo函数类获取窗口类的所有信息。当然系统还提供了一个直接从窗口获取所属类信息的方法:
DWORD GetClassLong(
HWND hWnd, //窗口句柄
int nIndex // 欲获得信息的表识
);
ULONG_PTR GetClassLongPtr(
HWND hWnd, // handle to window
int nIndex // offset of value to retrieve
);
上面函数中的索引nIndex可以为如下列表中的任意一个:
GCW_ATOM 窗口类的唯一标识(原子),由函数 RegisterClassEx 函数返回
GCL_CBCLSEXTRA 窗口类的扩展信息
GCL_CBWNDEXTRA 窗口的扩展信息
GCL_HBRBACKGROUND 窗口的背景画刷
GCL_HCURSOR 窗口的鼠标指针句柄
GCL_HICON 窗口的图标句柄
GCL_HICONSM 窗口最小化时的图标句柄
GCL_HMODULE 应用程序句柄
GCL_MENUNAME 窗口菜单句柄
GCL_STYLE 窗口类的式样(不是窗口的式样)
GCL_WNDPROC 窗口的回调函数
除了可以获取一个窗口所属的窗口类的信息外,对于一些信息还可以进行设置和改变, 这就可以通过如下函数来操作:
DWORD SetClassLong(
HWND hWnd, // handle to window
int nIndex, // index of value to change
LONG dwNewLong // new value
);
ULONG_PTR SetClassLongPtr(
HWND hWnd, // handle to window
int nIndex, // index of value to change
LONG_PTR dwNewLong // new value
);
需要注意的是并不是所有窗口类属性都可以被改变,而且这个改变只是改变这个窗口中的窗口类信息,并不会影响其他窗口对象的窗口类信息的数据,那么这种改变的作用在哪里呢?这就是下面要提到的窗口子类化的概念。
窗口子类化
我们知道任何一个窗口实例,都是某个窗口类下的实例,而每个窗口类在注册时就指定了窗口实例被创建时的一些特性,比如窗口事件处理回调函数,比如窗口的背景刷句柄等等。。 所有同类型窗口类下的窗口实例对象的这些机制都是一致的。但是在实际中有可能会想要解决窗口类下的某个特定的窗口实例需要具有不同的处理逻辑,尤其是事件处理回调函数。关于这些某些窗口需要进行特定处理的机制就称为窗口的子类化,对于窗口子类化最多的需求就是特化某个窗口的事件处理逻辑,也就是需要修改某个窗口的事件处理回调函数。为了解决这个问题,系统为窗口对象提供了一个SetClassLong/SetClassLongPtr函数来进行窗口类信息的修改机制。就如要修改某个窗口的事件处理回调函数时,我们可以用如下代码来实现:
long CALLBACK WndProcFn(HWND hWnd,UINT uID,WPARAM wParam,LPARAM lParam)
{
//特定于某个窗口的事件处理逻辑。
}
//执行下面的设置,只有hWnd窗口是使用WndProcFn方法,其他默认的同类型的窗口实例则使用默认的事件处理函数。
SetClassLongPtr(hWnd, GCL_WNDPROC, WndProcFn);
在MFC中我们大量的用到了子类化相关的技术,以及当我们想修改系统默认的控件的事件处理逻辑时就可以借助子类化技术来实现。
网友评论