欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从零开始的WTL入门教程(2)创建第一个窗口

程序员文章站 2022-05-29 09:25:53
...

在开始创建窗口前 有一些必须了解的C/C++特性,即使看不太懂也没有关系先记下

一.C++中的变量。

如果你没有C++基础 那么你需要重新理清C++中的变量和对象关系。
对于变量 它在创建的时候在栈中分配内存。C++中的对象可与基本类型一样直接创建,创建时即分配内存,离开作用域时出栈释放。也可以在堆中创建即创建指针指向new的对象

void founction(){
yourClass obj1;
yourClass *obj2 = new class;
}//obj1释放,obj2指针释放 ,*obj2对象内存溢出
二.模板。

WTL是基于模板开发,模板是实现泛型编程的基础。可以将模板看成一种描述方式,用于描述一个类型的对象遵循模板类型对象的类型声明。
举个例子

//一个单纯的类A
class A {
void functionA(){
}
}

//一个单纯的类B
class B {
void functionB(){
}
}
//一个单纯的类P
class P {
void functionP(){
}
}

//准备一个遵循T模板的类C
template<class T>//声明模板
class C:public T{
void functionC(){

}
}
//自己定义Class D
class D :public C<A>{
}
//D就是一个包含 functionC,functionA的类这样的描述指明了D继承于C 而C继承于A同样
//自己定义Class E
class E :public C<B>{
}
//E则是包含 functionC,functionB的类

模板可以用于描述一种继承关系,也就可以在使用的时候再去定义一个对象继承于谁

//自定义一个class F
template<class T /* = P */>//声明模板同时注释其类型 
class F :public T{
void functionF(){
//调用自己继承而来的functionP 继承于F的类必须指定其模板参数为P及P的子类
functionP()
}
//或用于方法
//创建一个R类型的对象并调用其founctionP方法
template <class R /* = P */>
void founctionF2(){
R objR;
objR.founctionP();
}

void functionF3(){
//调用时必须使用P或P的子类
founctionF2<P>();
}
}

这么做则相当于我们创建一个F类并且指定其继承于T类,并且T必须是一个P的子类因为我们在F中直接使用了P的方法。
模板主要是为了多态服务。极大的增强了代码的可复用性。而且模板的替换是静态的,在编译时就可以发现错误。
但是会造成继承关系不清晰,调试错误难以定位的问题,使用错误会导致问题定位到模板中,因此,不要设计业务十分复杂的模板类。
WTL中模板主要用于声明自定义控件的继承类型 。

三. 虚函数

对于一个类中的函数使用virtual关键字 则这个函数是一个虚函数

virtual void founction(){};//虚函数
virtual void founction() = 0;//纯虚函数

虚函数可以视作是一种接口声明。
纯虚函数更多是一种描述性用途,它规定了包含纯虚函数的类为基类 ,不可直接使用只能使用其子类。

回到WTL

在理解以上三点后 就可以开始了解WTL界面搭建的基本工具了。
在Win程序中 一切界面元素皆是CWindow。在WTL中最基本的界面类是CWindowImpl
在正式开始使用前先简单了解一下CWindowImpl
从零开始的WTL入门教程(2)创建第一个窗口

它有三个模板参数 其中 T自定义的类 TBase 是WinAPI中的窗口类 TWinTraits 是设置模板,可以不使用 先无视它。
从它的定义可以看出来两个泛型参数 TBase用于类型传递,T则用于本地调用,让CWindowImpl能够调用到你自定义的类的一些类方法,如果你重写了这些方法。防止子类重写这些方法后调用的仍然是父类的这些方法。
接着向上看
从零开始的WTL入门教程(2)创建第一个窗口
这里给与了Tbase以及TWinTraits 初始值因此如果你不写这两个参数,才不会报错。这个类主要是设置窗口的一些默认属性。可以不用关心他。接下来它又将TBase传递给了CWindowImplRoot。接着向上看。
从零开始的WTL入门教程(2)创建第一个窗口
这样我们就找到了最终的目的地 传递到这里的TBase指定了自定义控件的实际基类。
并且它同时继承于CMessageMap马上就会用到。我们先看一看它。
从零开始的WTL入门教程(2)创建第一个窗口
CMessageMap 中 定义了一个纯虚函数ProcessWindowMessage。也就是这里让我们必须使用CWindowImpl的子类并且必须实现ProcessWindowMessage方法。CMessageMap贴心的提供了一整套宏命令用来实现消息和消息转发。后面用到时会讲。
整理一下:
继承于CWindowImpl的类需要至少传三个参数 <T,TBase,TWinTraits > 其中 T 用于 CWindowImpl 调用。TBase 必须是 CWindow 及它的子类,有默认值CWinodw,实际用途为传递到最后给CWindowImplRoot并被它继承为该自定义类的基类。TWinTraits用于设置样式有默认值。

定义第一个自定义的窗口

从零开始的WTL入门教程(2)创建第一个窗口
此时创建了一个FirstWindow 类,它是一个基本的WTL对象。继承于CWindow
由于CMessageMap中的纯虚函数存在,因此必须添加
宏命令对其做出实现。

BEGIN_MSG_MAP(FirstWindow)
END_MSG_MAP()

这些是自动创建的构造函数和析构函数。如无需要,可以删掉。

	FirstWindow();
	~FirstWindow();
FirstWindow::FirstWindow()
{
}

FirstWindow::~FirstWindow()
{
}

练习项目中可以将代码全部写在.h文件。

然后使用它

WinMain函数中,Moudle创建后,消息循环开始执行前创建并显示这个窗口
从零开始的WTL入门教程(2)创建第一个窗口

其中CRect是描述位置大小的对象,可以设置上下左右边相对于父控件的位置。
创建了Window对象后需要调用Create方法创建窗口
该方法有以下几个参数

         In_opt_ HWND hWndParent, //父控件的句柄
		_In_ _U_RECT rect = NULL,//指定位置大小
		_In_opt_z_ LPCTSTR szWindowName = NULL,//窗口名称,会显示在左上角系统栏。默认样式可见
		_In_ DWORD dwStyle = 0,//窗口风格类型,属性设置 
		_In_ DWORD dwExStyle = 0,//窗口扩展风格
		_In_ _U_MENUorID MenuOrID = 0U,//资源ID,可以理解为该对象的数字标记,后面使用资源文件的时候会再次用到
		_In_opt_ LPVOID lpCreateParam = NULL//16位机遗留参数。现在用不着

这里我简单解释一下句柄
句柄是是一个结构体表示一个Window资源,也就相当于它的索引,通过句柄可以找到这个资源。也就可以通过句柄发收消息。CWindow对运算符进行了重载。使得CWindow的赋值操作实际只是让另一个CWindow对象的句柄指针指向目标对象的句柄。暂时了解一下就行了。
从零开始的WTL入门教程(2)创建第一个窗口

在Create方法中指定句柄通常用于设置控件位置,也就是说 rect参数设置的控件位置是相对于 hWndParent 的。而指定hWndParent为空的时候,窗口的位置是相对于屏幕的,左上角为0.0点,向右/向下为正轴
此时我们添加的代码创建了一个从屏幕左上角X/Y轴均为以10为起点到500为终点的FirstWindow类型的窗口。执行一下看看
从零开始的WTL入门教程(2)创建第一个窗口
看起来左侧边距似乎是要比顶部多一些,这是因为系统窗口的样式是有边框的,分布在左右下侧,在Win10中为透明样式,大约8像素
窗口缺了一个关闭按钮
可以在创建时dwStyle属性中设置该参数。可以使用 | 按位操作同时设置多个 。然后可以可以使用X按钮关闭窗口。

Window.Create(NULL, rc, "HELLO WORLD",WS_VISIBLE | WS_SYSMENU,NULL,0U,NULL );

从零开始的WTL入门教程(2)创建第一个窗口

但是这并不能关闭程序!
点击X只是隐藏了窗口,程序仍然在主方法中循环,没有任何变量被释放。
从零开始的WTL入门教程(2)创建第一个窗口
我们要在关闭窗口的时候关闭程序,就要用到消息机制。
在WTL窗口中的生命周期方法,界面交互都有对应的消息分发到对应的类中。这些消息的接收转发,都被封装为一系列的宏命令,将其插入到BEGIN_MSG_MAPEND_MSG_MAP之间并实现对应的方法即可接收消息
从零开始的WTL入门教程(2)创建第一个窗口
PostQuitMessage(0) 是WinApi方法,可以结束程序。
MSG_WM_CLOSE(OnClose) 声明了由OnClose方法接收关闭窗口的消息。
可以更改方法名,但是一般使用原头文件中的方法名以便阅读
同理可以推得,如窗口创建,移动,绘制鼠标时间等大量的生命周期/操作相应等系统管理的事件都可以通过这个机制来操作
从零开始的WTL入门教程(2)创建第一个窗口
可以直接去对应的头文件中找到消息和消息参数定义。
但是要注意 调用该方法结束应用前必须删除掉所有你创建的窗口
从零开始的WTL入门教程(2)创建第一个窗口
DestroyWindow() 可以删除当前窗口,也可以填入一个窗口的句柄用于删除指定窗口。
从零开始的WTL入门教程(2)创建第一个窗口
从零开始的WTL入门教程(2)创建第一个窗口
从零开始的WTL入门教程(2)创建第一个窗口

相关标签: WTL/C++