Notepad++源代码阅读——窗口封装与继承

引言

近期在看Notepad++的源代码,学习学习Win32 原生API的开发技巧。

本文以Notepad++ 1.0版本的源代码为例讲解如何封装windows窗口,实现面向对象开发,如何通过窗口的继承实现代码的重用,并且利用C++的动态绑定特性实现多态,另外说明窗口封装过程中如何封装消息处理程序,这是实现面向对象的关键所在。听我细细道来。

实现窗口类

下图是Notepad++1.0版本窗口类的继承层次:

在Notepad++ 1.0 中所有的窗口元素:编辑窗口、选项卡窗口、工具栏、状态栏、对话框等等都有一个共同的父类:Window类,该类是一个虚基类,不能被实例化,其中的detroy函数是纯虚函数。里面声明了每个窗口所必须包含的变量:自身的窗口句柄_hSelf,父窗口句柄 _hParent 和 整个程序的实例句柄 _hInst。该类实现了一些窗口的基本操作,部分为虚函数。下面我们看看它的源代码:

#include <windows.h>

class Window //虚基类
{
public:
    Window():_hSelf(NULL), _hParent(NULL), _hInst(NULL){};    // 构造函数,在子类中的构造函数调用,为三个变量赋值,
    virtual ~Window() {};

    virtual void init(HINSTANCE hInst, HWND parent)    // 虚函数、子类中实现自己的版本,如注册窗口,创建窗口等等
    {
        _hInst = hInst;
        _hParent = parent;
    }

    virtual void destroy() = 0;    // 资源释放等等

    virtual void display(bool toShow = true) const {// 显示窗口
        ::ShowWindow(_hSelf, toShow?SW_SHOW:SW_HIDE);
    };

    virtual void reSizeTo(RECT & rc) // should NEVER be const !!!
    {                                // 这里特别强调rc不能为 const, 因为有时候要通过它返回
                                    // 它上面的客户区,让客户上的窗口重置大小。如选项卡窗口
                                    // reSizeTo返回选项卡的客户区、编辑窗口用返回的矩形区域
                                    // 重置大小
        ::MoveWindow(_hSelf, rc.left, rc.top, rc.right, rc.bottom, TRUE);
        redraw();
    };

    virtual void redraw() const {        // 强制刷新窗口
        ::InvalidateRect(_hSelf, NULL, TRUE);
        ::UpdateWindow(_hSelf);
    };

    virtual void getClientRect(RECT & rc) const {    // 得到用户区矩形
        ::GetClientRect(_hSelf, &rc);
    };

    virtual int getWidth() const {
        RECT rc;
        ::GetClientRect(_hSelf, &rc);
        return (rc.right - rc.left);
    };

    virtual int getHeight() const {
        RECT rc;
        ::GetClientRect(_hSelf, &rc);
        return (rc.bottom - rc.top);
    };

    virtual bool isVisible() const {
        return bool(::IsWindowVisible(_hSelf));
    };

    HWND getHSelf() const {    // 得到自身窗口句柄
        if (!_hSelf)
        {
            ::MessageBox(NULL, "_hSelf == NULL", "class Window", MB_OK);
            throw int(999);
        }
        return _hSelf;
    };

    void getFocus() const {
        ::SetFocus(_hSelf);
    };

    HINSTANCE getHinst() const {
        if (!_hInst)
        {
            ::MessageBox(NULL, "_hInst == NULL", "class Window", MB_OK);
            throw int(1999);
        }
        return _hInst;
    };
protected:
    HINSTANCE _hInst;    // 程序实例句柄
    HWND _hParent;        // 父窗口句柄
    HWND _hSelf;        // 自身窗口句柄
};

这就是窗口的基类,用这个基类我们就能派生出自己的实现特定功能的窗口。下面讲解几个典型的窗口。

对话框的封装

Notepad++ 的对话框继承StaticDialog,StaticDialog又继承上面的Window类。对话框基类的声明如下:

class StaticDialog : public Window
{
public :
    StaticDialog() : Window() {};
    ~StaticDialog(){};
    virtual void create(int dialogID);
    virtual bool isCreated() const {
        return reinterpret_cast<bool>(_hSelf);
    };
    //virtual do
    void destroy() {
        ::DestroyWindow(_hSelf);
    };
protected :
    static BOOL CALLBACK dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
    virtual BOOL CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) = 0;
};

对话框的封装关键在于create函数的实现。该函数传入对话框的资源ID然后创建,函数实现如下:

void StaticDialog::create(int dialogID)
{
    _hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent,  (DLGPROC)dlgProc, (LPARAM)this);

    if (!_hSelf)
    {
        systemMessage("StaticDialog");
        throw int(666);
    }
    display();
}

函数基本就是对话框创建的API调用,传入对话框资源、消息处理程序:dlgProc,这个函数是静态 static 函数,因此可以传入该函数调用,最后将this 指针传入其中,WM_INITDIALOG消息中可以获取这个指针。

下面看看dlgProc 的实现:

BOOL CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_INITDIALOG :
        {
            StaticDialog *pStaticDlg = (StaticDialog *)(lParam);
            pStaticDlg->_hSelf = hwnd;
            ::SetWindowLong(hwnd, GWL_USERDATA, (long)lParam);
            pStaticDlg->run_dlgProc(message, wParam, lParam);
            return TRUE;
        }

        default :
        {
            StaticDialog *pStaticDlg = reinterpret_cast<StaticDialog *>(::GetWindowLong(hwnd, GWL_USERDATA));
            if (!pStaticDlg)
                return FALSE;
            return pStaticDlg->run_dlgProc(message, wParam, lParam);
        }
    }
}

在WM_INITDIALOG 消息中将lParam转换成StaticDialog指针,这样就能获取窗口句柄_hSelf(基类成员), 同时将指针放在USERDATA中,在其他消息中取出,指针并调用成员函数:run_dlgProc,这个函数是纯虚函数,继承的对话框子类就能实现自己的特定消息处理了。这个就是消息处理程序的封装。在最后我们还将讲解主窗口的消息处理的封装,其实和对话框所用的方法大同小异。

选项卡窗口

写累了,待续

主窗口类

写累了,待续

封装消息处理程序(Encapsulating WndProc)

这里已Notepad++ 1.0 版本的代码讲解如何封装窗口消息处理程序。

写累了,待续

Notepad++源代码阅读——窗口封装与继承

时间: 2024-10-25 03:30:17

Notepad++源代码阅读——窗口封装与继承的相关文章

Notepad++源代码阅读——窗口元素组织与布局

1.1 前言 这两天在看notepad++ 1.0版本的源代码.看了许久终于把程序的窗口之间的关系搞清楚了现在把其组织的要点写于此,希望对大家有所帮助. 1.2 窗口元素之间的关系 Notepad++主要有以下窗口元素(见下图). 其中Notepad_plus 是程序的主要窗口,其他:工具栏.状态栏.主次编辑窗口.主次选项卡窗口以及对话框窗口均为主窗口的子窗口.     _mainDocTab 和 _subDocTab 为 类:DocTabView 其成员_pView 分别指向 _mainEdi

OpenJDK 源代码阅读之 Collections

概要 类继承关系 java.lang.Object java.util.Collections 定义 public class Collections extends Object 实现 sort public static <T extends Comparable<? super T>> void sort(List<T> list) { Object[] a = list.toArray(); Arrays.sort(a); ListIterator<T&g

类和对象、封装、继承、多态

? 类和对象 ? 类和对象的概念 类:客观存在的,抽象的,概念的东西.一个类可以被定义为描述行为的模板: 对象:对象是具体的,实际的,代表一个事物.对象具有状态和行为. 类是对象的模板,对象是类的一个具体实体. 定义Java中的类 一个类可以包含以下任意变量类型. a)局部变量: 方法里面,构造函数或块中定义的变量称为局部变量.该变量将被声明和初始化的方法中,当该方法完成该变量将被销毁. b)实例变量: 实例变量都在一个类,但任何方法之外的变量.这些变量在类被加载的实例化.实例变量可以从内部的任

面向对象的三大特征——封装、继承、多态

接触过面向对象的人都知道面向对象有三大特征,分别是封装.继承和多态.这三者分别指的是什么,为什么是这哥仨,使用他们有什么好处,我们来梳理一下. 封装 原则:隐藏对象的属性和实现细节,仅对外提供公共访问方式. 好处: ①将变化隔离. ②便于使用. ③提高重用性. ④提高安全性. Demo1: 比如一个学生类:他具有姓名和年龄两个属性,但是为了提高安全性和重用性,便于使用,我们将其封装起来. public class Student { private String name; private in

三维引擎设计-图形窗口封装

一:概述: 每个操作系统都有自己的图形系统,三维引擎会抽象出一个窗口,然后通过继承的方式,子类分别封装不同平台下面的窗口,另外,三维图形API也支持将内容渲染到其他表面上,比如纹理中,所以三维引擎也会抽象出一个纹理,再通过继承的方式,由子类分别封装不同图形API的纹理. 窗口和纹理,都可以看成一个抽象的画布,接收三维引擎的结果渲染到这个画布上,总体来说,一个抽象的画布,代表抽象的窗口或纹理,不同平台和图形API下的窗口和纹理又子类实现.这是三维引擎封装窗口系统的一种方法. 二:OSG的设计: 1

淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成物理查询计划

SQL编译解析三部曲分为:构建语法树,制定逻辑计划,生成物理运行计划. 前两个步骤请參见我的博客<<淘宝数据库OceanBase SQL编译器部分 源代码阅读--解析SQL语法树>>和<<淘宝数据库OceanBase SQL编译器部分 源代码阅读--生成逻辑计划>>.这篇博客主要研究第三步,生成物理查询计划. 一. 什么是物理查询计划 与之前的阅读方法一致,这篇博客的两个主要问题是what 和how.那么什么是物理查询计划?物理查询计划可以直接运行并返回数据

黑马程序猿——JAVA面向对象的特性:封装,继承,多态

                                       - ----------android培训.java培训.java学习型技术博客.期待与您交流!------------  一.java面向对象的特性.封装.继承.多态         封装 封装是对象和类概念的主要特性. 封装.也就是把客观事物封装成抽象的类.而且类能够把自己的数据和方法仅仅让可信的类或者对象操作,对不可信的进行信息隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承

vb中的封装,继承,多态的实现。

        VB中引入了面向对象的编程机制,使该语言具有面向对象语言的封装.继承和多态三个主要的特征,极大地提高了应用程序的开发效率.但是大多数面向对象的设计语言都通过继承来提供多态的,而vb是通过多从ActiveX接口来提供多态的.         在上一篇文章中我简单的描述了vb中的类与对象,类,对象,对于继承与多态来说是比不可少的,下面我谈一下vb中封装,继承,多态的实现.         1. 封装         从表面意思上看就是将某些东西给打包封起来,vb中的封装与这个意思大致

【转】Tomcat总体结构(Tomcat源代码阅读系列之二)

本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Eclipse中运行Tomcat源代码,本文介绍一下Tomcat的总体结构. 本文没有特别指明的地方,源代码都是针对tomcat7.0.42来说. Tomcat的总体结构 Tomcat即是一个Http服务器也是一个Servlet容器,它的总体结构我们可以用下图来描述: 通过上图我们可以看出Tomcat中