教程 - 软件 - 文章 - 论坛

::白话C++::

更多感受-Hello VCL (一)

Hello VCL (二) >>

VCL:Visual Component Library(可视化控件库)。以下英文是Borland自己的解释:

VCL is an acronym for the Visual Component Library, a set of visual components for rapid development of Windows applications in the Delphi language. VCL contains a wide variety of visual, non-visual, and utility classes for tasks such as Windows application building, web applications, database applications, and console applications.

事实上,在《Hello Windows》《Hello Internet》《Hello Database》等课程中……我们一直在用VCL(价格便宜量又足)。

所谓不怕不知货,就怕货比货,下面我们来比较一下三种最为通行的Windows界面编写方法:

  1. Windows原生API方式,也就是直接使用Windows提供的函数来实现。
  2. MFC——不用多说了吧,用过微软Visual C++的人都知道。
  3. VCL

我们的目标是,假设在做界面时,需要修改一下当前窗口要输出的字体。事实上,在《Hello Windows》等章节里,我们已经有过多次通过“对象监视器”修改某个Label字体的经验——这样做当然是最简单直观的,不过三者中,仅VCL支持,出于公平起见,让我们都用代码来说话。

修改字体的接口
API  
HFONT CreateFont( 
	int nHeight, // height of font
	int nWidth, // average character width
	int nEscapement, // angle of escapement
	int nOrientation, // base-line orientation angle
	int fnWeight, // font weight
	DWORD fdwItalic, // italic attribute option
	DWORD fdwUnderline, // underline attribute option
	DWORD fdwStrikeOut, // strikeout attribute option
	DWORD fdwCharSet, // character set identifier
	DWORD fdwOutputPrecision, // output precision
	DWORD fdwClipPrecision, // clipping precision
	DWORD fdwQuality, // output quality
	DWORD fdwPitchAndFamily, // pitch and family
	LPCTSTR lpszFace // typeface name
);
MFC  
BOOL CFont::CreateFont (int nHeight,
	int nWidth,
	int nEscapement,
	int nOrientation,
	int nWeight,
	BYTE bItalic,
	BYTE bUnderline,
	BYTE cStrikeOut,
	BYTE nCharSet,
	BYTE nOutPrecision,
	BYTE nClipPrecision,
	BYTE nQuality,
	BYTE nPitchAndFamily,
	LPCTSTR lpszFacename
);
VCL
 
	Font->Name = “宋体”; //字体名
	Font->Charset = GB2312_CHARSET; //字符集
	Font->Size = 24; //尺寸
	Font->Style = Font->Style << fsBold;//粗体风格

孰简孰繁?一目了然。(事实上,API和MFC仅是列出创建指定字体的函数声明,实际使用时,还需更多代码)。其实,将Windows API做一层“面向对象”的封装,必是大势所趋,MS后来在推的.NET,正是在完成这件事情,只是.NET的封装,未免太“厚”了!

Hello系列的收场章节:“Hello VCL”正题即将开始,您泡好茶或咖啡了吗?

第5小节:更多感受-Hello VCL

5.1 需求

大家有没有遇上这种事?在电脑面前一坐就是数小时,结果忘了其它紧要的事。比如我在这里写课程,结果阳台洗衣机的水一直哗哗的流着,更惨的是厨房里烧开水把铝壳都给烧穿底……如果有个小工具,能让我们事先加个备忘,差不到时间后提醒一下我们,该多好啊!

本小节,我们就用VCL来写一个“定时提醒助手”,出于“感受”的需要,代码各个主要功能点,我都将用VCL提供的函数或控件来实现。称得上是一个“原汁原味”的VCL工程。

设计和代码对C++ Builder6 和Turbo C++ Exploeres都通用,我这里仅用后者讲解。

首先请选建立一个“VCL Forms Application”;CB6用户请参看《Hello Windows》

5.2 主面设计

首先看一眼最终设计结果图:

(定时提醒小助手界面设计)

下面挑要点讲解,对于我们熟悉的控件,比如TLabel,TButton,或熟悉的属性,比如位置、大小等,下面讲解中将略过。

Step1:主窗口,也就是默认产生的Form1属性设置。

属性 说明
Caption 定时提醒小助手  
Font 宋体、CHINESE_GB2312、五号 在字体选择框内设置
Position poDesktopCenter 用于让窗口自动显示在屏幕中间
BorderStyle bsSingle 可以最小化,但不能改变窗口大小
BorderIcons 将biMaximize设置为False 不允许窗口最大化
Scaled False 窗口不因屏幕分辨率改变而缩放

Step2:添加TListView控件,并设置属性。

界面中占最大面积的控件为TListView,即截图中的ListView1控件,它来自控件栏的“Win32”页。

属性 说明
ReadOnly true 只读,不允许用户直接修改
RowSelect true 用户点击某一行时,选中整行,而不是仅选中第1列
ViewStyle vsReport 类似于在Windows的文件夹中,采用“详细信息”查看文件

Step3:添加ListView1各列。

双击ListView1控件,在弹出窗口中,添加“状态、内容、时间”三项,并在“对象检视器”中,将其宽度(Width)属性至合适值,在上图中,我设置的宽度分别是:80、250、150;然后关闭弹出窗口。

(为ListView添加三个列头)

Step4:添加4个按钮。

如前述界面设计图所示。并且图中箭头所指,分别修改4个Button的Name属性为:ButtonAbout、ButtonAdd、ButtonRemove、ButtonEdit。然后修改它们的标题(Caption),比如最左边的按钮标题为:“关于(&U)”。

Step5:添加ImageList控件。

通过ImageList控件,可以为ListView1的各行的首列添加一个小图标,用于表示其状态。同样在“Win32”页,找到TImageList控件,并添加到Form1上,然后双击它,出现对话框:

(为ImageList1添加图标)

如果找不到图上那三个小闹钟的图标,点击这里下载:“《定时提醒小助手》闹钟状态图”。由于我将三张图合成一张,所以在导入时,会出现“是否将大图一拆为三”的提问,请选择“Yes”。 如果没有出现提问,请检查ImageList1的Width和Height属性是否都为16。

完成后,再设置ListView1的“StateImages”属性为“ImageList1”。如图:

(设置StateImages属性值为ImageList1)

现在还差一个Timer1和XPManifest1控件,留在后面讲。

5.3 “提醒项”数据定义

Step1:新增单元文件。

为了不至于将“业务逻辑”和“界面设计”的代码混在一起,我们需要单独创建一个新CPP代码文件,用于专门实现相关的数据定义。

在Turbo C++ Explores中,选择主菜单:File->New->Unit。新建一个CPP代码文件,并自动打开在代码编辑窗口中。

Step2:头文件

使用Ctrl + F6,或直接从代码编辑窗口底部选择,确保切换到头文件Unit2.h (您新建CPP文件,有可能不叫Unit2)。

(切换到头文件)

然后在头文件中,输入以内容,注意部分内容可能原来就存在,其中前面的两处“Unit2H”,应以您愿来的内容为准。

 
//---------------------------------------------------------------------------
//文件: Unit2.h
#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------

struct AlertItem
{
	AlertItem()
			: status(waitting)
	{}

	~AlertItem()
	{}

	//定义提醒项的三种状态:未提醒,正在提醒,已过期:
	enum Status {waitting, alertting, past};

	static AnsiString GetStatusCaption(Status status);

	Status status;
	AnsiString caption;
	TDateTime time;
};

struct AlertList
{
public:
	AlertList();
	~AlertList();

	//添加一个新的提醒项.指定提醒内容,和提醒时间
	AlertItem* AddItem(AnsiString const caption
					   , TDateTime const time);

	//获得第index条的提醒项:
	AlertItem* GetItem(int index);
	//删除第index条的提醒项:
	void RemoveItem(int index);
	//删除全部提醒项:
	void RemoveAll();

	//得到个数:
	int GetCount() const
	{
		return lst->Count;
	}

	//保存到文件:
	void Save(AnsiString const & fileName);
	//从文件中加载:
	void Load(AnsiString const & fileName);

private:
	TList* lst;
};

#endif

(Unit2.h)

通过上一节课《Hello OO》,您应该能看到在头文件里,我们定义了两种“类型”:AlertItem 和 AlertList。

我们要实现的小工具,支持用户添加多个“提醒项”,AlertItem定义的就是“提醒项”这个类型。而AlertList则定义一个列表“类型”,这种类型的功能是用来存储多个“AlertItem”。VCL提供了TList类型,用来实现列表式的存储功能,我们定义AlertList这个类型,内部其实是通过TList来实现存储(当然,我们也可以使用STL提供的list来实现,请参看《Hello STL》,但这里让我们尽量展示一下VCL的功能)。

Step3:源文件代码。

再次通过Ctrl+F6,或者同样从代码编辑窗口底部选择,确保切换到头文件Unit2.cpp。

(切换回CPP文件)

输入以下代码。注意部分代码可能原已存在,其中 #include "Unit2.h" 一行,应以原有内容为准。

 
//文件Unit2.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit2.h"
//---------------------------------------------------------------------------

#pragma package(smart_init)

AnsiString AlertItem::GetStatusCaption(AlertItem::Status status)
{
	AnsiString captions [] =
		{
			"等待", "警告", "过期",
		};

	return captions[status];
}

AlertList::AlertList()
{
	lst = new TList;
}

AlertList::~AlertList()
{
	RemoveAll();

	delete lst;
}

AlertItem* AlertList::AddItem(AnsiString const caption, TDateTime const time)
{
	AlertItem* item = new AlertItem;
	item->status = AlertItem::waitting;
	item->caption = caption;
	item->time = time;

	lst->Add(item);

	return item;
}

AlertItem* AlertList::GetItem(int index)

{
	if (index < 0 || index > lst->Count)
	{
		return 0;
	}

	return (AlertItem *)lst->Items[index];
}

void AlertList::RemoveItem(int index)
{
	AlertItem* item = GetItem(index);

	if (item != 0)
	{
		delete item;
		lst->Delete(index);
	}
}

void AlertList::RemoveAll()
{
	while (lst->Count > 0)
	{
		RemoveItem(0);
	}
}

//保存到文件:
void AlertList::Save(AnsiString const & fileName)
{
	TFileStream* fs = new TFileStream(fileName, fmCreate);

	int ver = 1;
	fs->Write(&ver, sizeof(ver));

	int count = GetCount();
	fs->Write(&count, sizeof(count));

	for (int i = 0; i < count; ++i)
	{
		AlertItem* item = GetItem(i);

		int len = item->caption.Length();
		fs->Write(&len, sizeof(len));
		fs->Write(item->caption.c_str(), len);

		fs->Write(&item->status, sizeof(item->status));
		fs->Write(&item->time.Val, sizeof(item->time.Val));
	}

	delete fs;
}

//从文件中加载:
void AlertList::Load(AnsiString const & fileName)
{
	if (!FileExists(fileName)) //判断文件是否存在?
	{
		return;
	}

	TFileStream* fs = new TFileStream(fileName, fmOpenRead);

	int ver;
	fs->Read(&ver, sizeof(ver));

	int count;
	fs->Read(&count, sizeof(count));

	for (int i = 0; i < count; ++i)
	{
		AlertItem* item = new AlertItem;

		int len;
		fs->Read(&len, sizeof(len));

		char *buf = new char[len + 1];
		memset(buf, 0, len + 1);

		fs->Read(buf, len);
		item->caption = buf;
		delete[] buf;

		fs->Read(&item->status, sizeof(item->status));
		fs->Read(&item->time.Val, sizeof(item->time.Val));

		lst->Add(item);
	}

	delete fs;
}

(Unti2.cpp)

后续内容见:Hello VCL (二) >>

版权所有 谢绝复制。作者:南郁(nanyu) www.d2school.com