更多感受-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界面编写方法:
- Windows原生API方式,也就是直接使用Windows提供的函数来实现。
- MFC——不用多说了吧,用过微软Visual C++的人都知道。
- 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 (二) >>。 |