Windows窗口类

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中我们大量的用到了子类化相关的技术,以及当我们想修改系统默认的控件的事件处理逻辑时就可以借助子类化技术来实现。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我的博客

Dedecms普通模型入门教程

1. 默认模板设置 里面是default后面变量名字是cfg_df_style(在模版中使用方法是{dede:golbal.cfg_df_style/}获取的路...

37860
来自专栏移动开发之家

Flutter完整开发实战详解(二、 快速开发实战篇)

 作为系列文章的第二篇,继《Flutter完整开发实战详解(一、Dart语言和Flutter基础)》之后,本篇将为你着重展示:如何搭建一个通用的Flutter ...

1.7K10
来自专栏angularejs学习篇

angularjs学习第四天笔记(第一篇:简单的表单验证)

您好,我是一名后端开发工程师,由于工作需要,现在系统的从0开始学习前端js框架之angular,每天把学习的一些心得分享出来,如果有什么说的不对的地方,请多多指...

12620
来自专栏小狼的世界

封装内容和功能 – YUI TabView使用小记

本文主要内容取自 Caridy Patino 在2008年发布的文章,原文中使用的是YUI2,笔者对例子做了一些更新,均使用了YUI3.1.1,文章中讨论的这个...

11620
来自专栏vue学习

24、商品列表页之数据渲染和传值

(1)data中定义一个list对象 (2)将res.data.goodslist赋值给list (3)我们将商品图片、文字描述、价格、折扣等等信息传给子组...

16210
来自专栏逸鹏说道

Jupyter ~ 像写文章般的 Coding

这次选Markdown模式(关于Markdown基础可以看之前写的Markdown Base)

13530
来自专栏逸鹏说道

Jupyter ~ 像写文章般的 Coding (附:同一个ipynb文件,执行多语言代码)

这次选Markdown模式(关于Markdown基础可以看之前写的Markdown Base)

2.9K60
来自专栏Python攻城狮

使用Selenium抓取QQ空间好友说说1.安装Selenium2.在Python中使用Selenium获取QQ空间好友说说3.代码实现(基于Python3)

通过Robo 3T(数据库MongoDB的一款功能强大的数据库管理工具)可以看到我们已经将拿到的数据库存储于数据库中

12220
来自专栏Golang语言社区

Golang语言社区--golang 进度下载文件

大家好,我是Golang社区主编彬哥,本篇给大家转载一篇关于文件下载相关的文章。

52460
来自专栏宏伦工作室

解放你的双手,陪爸妈看春晚去!

22320

扫码关注云+社区

领取腾讯云代金券