副标题#e#
1 媒介
作为和delphi雷同的rad(rapid application development)东西,c++ builder的强大成果不只表此刻数据库开拓方面,也凸现于应用措施开拓上(令人称绝的是这两方面团结得很是好)。仅就应用措施而言,要真正浮现c++ builder的优势,开拓出高质量的软件,则在拖拉拽放之外,尚需用到一些进阶技能。如动静处理惩罚、dll、ole、线程、sdk编程。c++ builder在这些方面都或多或少有独到的优势。另外,可以利便地建造自界说控件,也是c++ builder的一大特色和高级成果。本文将通过建造一个标题棒在窗口左边的对话框控件,来示范一些c++ builder中关于控件建造和动静处理惩罚的观念,同时涉及到一点sdk编程。我们将要建造的是一个对话框,就如同opendialog等一样,一挪用其execute()要领,就弹出一个如图一所示的窗口。这个窗口的标题棒位于左方,绿色,文字走向为由下而上的90度字形,其成果和一般的标题棒沟通,可以將鼠标移至该处来移动该窗口。
首先来完成这个窗口,然后用它来建造对话框控件。
2 操作wm_nchittest动静建造竖直标题的窗口
.wm_nchittest动静
c++builder将某些windows动静封装于事件(event)中,但无法席卷所有动静,如wm_nc**** 系列动静。wm_nchittest动静产生于游标(cursor)移动或鼠标按下、释放时,返回值指示今朝游标地址位置,如返回hthscroll暗示处于程度转动条内,返回htcaption暗示处于标题棒内(拜见win32 sdk help)。其参数xpos、ypos别离暗示游标的x、y坐标(相对付屏幕左上角),别离对应于lparam的低字和高字。假如拦截wm_nchittest动静,使恰当鼠标在窗口左边按下时,工钱地将返回值设为htcaption,则系统将觉得是在标题棒内,于是将可以移动窗口,完成了标题棒的成果,至于颜色和文字,则与动静无关,将在下面论述其道理。
#p#副标题#e#
.windows动静
动静就是windows操纵系统送往措施的事件。但事件数以百计,操纵系统并沒有为各个事件设计差异的动静布局,而是以一个一般性的布局来来描写动静,这个布局在c++ builder中界说为tmessage。别的c++ builder对常见动静界说了专用布局,二者对等。可以直接将动静转换为专用布局,也可以自行表明tmessage参数。以wm_nchittest动静为例,它的界说如下:
struct twmnchittest
{
cardinal msg;
long unused;
union
{
struct
{
windows::tsmallpoint pos;
long result;
};
struct
{
short xpos;
short ypos;
};
};
};
比较tmessage界说:
struct tmessage
{
cardinal msg;
union
{
struct
{
word wparamlo;
word wparamhi;
word lparamlo;
word lparamhi;
word resultlo;
word resulthi;
};
struct
{
long wparam;
long lparam;
long result;
};
};
};
可以发明,tmessage的lparam成员对应twmnchittest的pos成员,就是说以下两行语句
等价:
tpoint pt=tpoint(msg.lparam); //此时msg范例为tmessage
tpoint pt=tpoint(msg.pos); //此时msg范例为twmnchittest
.c++ builder处理惩罚动静的宏
在c++ builder中自界说动静处理惩罚是较为利便的,团结wm_nchittest举譬喻下:
在窗口类的protected部门插手如下宏界说:
begin_message_map
message_handler(wm_nchittest,tmessage,onnchittest)
end_message_map(tform)
message_handler包括3个参数:wm_nchittest,动静标识,也可觉得自界说动静如wm_mymessage,这时只需加一个宏如#define wm_mymessage wm_app+1等;第二个参数tmessage代表动静范例,也可觉得切合要求的自界说动静布局范譬喻tmymsg等,onnchittest为动静处理惩罚函数。这样,一旦有wm_nchittest动静传给tform,对该动静的响应就完全交由onnchittest函数处理惩罚。onnchittest函数只有一个参数,范例为message_handler中第2个参数的引用,即tmessage &或tmymsg &。
.完成图一的窗口。
开始一个新应用措施(new application),将form1定名为vcform,对应单位文件为vcap.cpp,头文件为vcap.h。vcform的boarderstyle配置为bsnone,其上安排一个位图按钮bitbtn1,caption为&ok,kind为bkok,onclick事件处理惩罚函数中插手一句close()。然后在vcap.h的protected部门插手如前所述动静处理惩罚宏和函数onnchittest的声明,以处理惩罚标题条的拖动成果。为完成标题的着色和文字输出,双击vcform的onpaint事件以定制formpaint函数,详细代码见下面源码。另外为使窗口有立体感,重载虚函数createparams,以修改窗口的气势气魄。完整的vcap.h和vcap.cpp如下:
//vcap.h
#ifndef vcaph
#define vcaph
#include
#include
#include
#include
#include
class tvcform : public tform
{
__published: // ide-managed components
tbitbtn *bitbtn1;
void __fastcall formpaint(tobject *sender);
void __fastcall bitbtn1click(tobject *sender);
private: // user declarations
protected:
void __fastcall onnchittest(tmessage & msg);
void __fastcall createparams(tcreateparams& params);
begin_message_map
message_handler(wm_nchittest,tmessage,onnchittest)
end_message_map(tform)
public: // user declarations
__fastcall tvcform(tcomponent* owner);
};
extern package tvcform *vcform;
#endif
//vcap.cpp
#include
#pragma hdrstop
#include "vcap.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
tvcform *vcform;
__fastcall tvcform::tvcform(tcomponent* owner)
: tform(owner)
{
}
void __fastcall tvcform::formpaint(tobject *sender)
{
//绘制宽20的绿色标题条
rect rc;
setrect(&rc,0,0,clientwidth,clientheight);
canvas->pen->color=clgreen;
canvas->brush->color=clgreen;
canvas->rectangle(0,0,20,clientheight);
//输出旋转文字
char* msg=caption.c_str();
logfont fontrec;
memset(&fontrec,0,sizeof(logfont));
fontrec.lfheight = -13;
fontrec.lfweight = fw_normal;
fontrec.lfescapement = 900; //旋转角度900x0.1度=90度
lstrcpy(fontrec.lffacename,"宋体");
hfont hfont=createfontindirect(&fontrec);
hfont hold=::selectobject(canvas->handle,hfont);
::setrect(&rc,0,0,20,clientheight);
::settextcolor(canvas->handle,rgb(255,255,255));
::textout(canvas->handle,3,clientheight-3,msg,lstrlen(msg));
::selectobject(canvas->handle,hold);
::deleteobject(hfont);
}
void __fastcall tvcform::bitbtn1click(tobject *sender)
{
close();
}
void __fastcall tvcform::onnchittest(tmessage & msg)
{
tpoint pt;
pt.x=loword(msg.lparam);
pt.y=hiword(msg.lparam);
pt =screentoclient(pt);
rect rc;
setrect(&rc,0,0,20,clientheight);
if (ptinrect(&rc,pt))
msg.result = htcaption;
else
defaulthandler(&msg);
}
void __fastcall tvcform::createparams(controls::tcreateparams& params)
{
tform::createparams(params);
params.style |= ws_popup;
params.style ^= ws_dlgframe;
}
#p#分页标题#e#
vcform的动静处理惩罚已经先容过,这里再对标题条的绘建造扼要说明。由于c++builder的tfont没有界说文字旋转旋转的属性,因此用传统的sdk画图要领。canvas->handle等于代表gdi画图的hdc。
3 建造对话框控件在开始建造控件之前,先将vcap.cpp中的#pragma package(smart_init)行注释掉。建设控件的步调是:建设一个单位文件,在个中完成控件的类界说和注册,然后就可以安装了。控件类一般从某个现有类担任导出。建造控件与一般类界说的主要区别在于属性(property)和事件(event),事件也是属性。由属性就带来了属性的存取要领、缺省值、属性编辑器等问题。为简朴起见,本控件只涉及到上述一部门观念,但能涵盖控件建造的一般进程。
.开始一个空控件
由于要建造的对话框控件的最小须要成果是一个execute()要领,因此可以从tcomponent类担任。定名控件名为tvcaptiondlg,界说控件的单位文件定名为vcapdlg.cpp,其头文件为vcapdlg.h。用component wizard或手工要领完成如下文件:
//vcapdlg.h
#ifndef vcapdlgh
#define vcapdlgh
#include
#include
#include
#include
class package tvcaptiondlg: public tcomponent
{
private:
protected:
public:
virtual __fastcall tvcaptiondlg(tcomponent *owner);
__published:
};
#endif
//vcapdlg.cpp
#include
#pragma hdrstop
#include "vcapdlg.h"
#pragma package(smart_init)
static inline tvcaptiondlg * validctrcheck()
{
return new tvcaptiondlg(null);
}
namespace vcapdlg //同控件界说单位文件名,首字母大写,其余小写
{
void __fastcall package register()
{
tcomponentclass classes[1]={__classid(tvcaptiondlg)};
registercomponents("mailuo",classes,0);
}
}
__fastcall tvcaptiondlg::tvcaptiondlg(tcomponent * owner)
:tcomponent(owner)
{
}
registercomponents("mailuo",classes,0)指示在控件面板上的mailuo页(没有则建设该页)上生成classes数组包括的所有控件,这里是一个tvcaptiondlg控件。虽然此时的tvcaptiondlg控件不具备tcomponent类之外的任何本领。
.将要用到的form文件包括进来
这只需在vcapdlg.cpp的#include "vcapdlg.h"后插手一行#include "vcap.cpp"(vcapdlg.*与vcap.*在同一目次)即可,重申一句:vcap.cpp中的#pragma package(smart_init)行要去掉。将整个vcap.cpp和vcap.h的内容包罗在vcapdlg.cpp中也是可以的,这样就用不着vcap.*文件了.即将类vcform的界说与vcapdlg放在一个文件里,横竖vcform只不外是vcapdlg要用到的一个类界说而已。不外这样一来,在生成vcform的实例工具时,上面所说bitbtn1的caption、kind等与缺省值不等的属性都需要运行时配置,因为非缺省属性是生存在.dfm文件里的。这也是利用了form的类常用单独的单位文件生存的原因。
.添加接口属性
#p#分页标题#e#
这里只提供一个caption属性供控件利用者用于读取或配置对话框的标题。为此只需在类tvcaptiondlg的声明体的private区插手一个ansistring fcaption变量作内部存储用,并在__published
区插手一行:
__property ansistring caption={read=fcaption, write=fcaption};
因为属性范例是ansistring,所以不需专门的属性编辑器处理惩罚设计时属性的编辑。别的在设计时该属性值的改变不需引起什么当即的处理惩罚和进程,因此存取要领回收最简朴的当即存取(read=fcaption,
write=fcaption)。
.添加执行要领
vcaptiondlg的execute()要领的成果是建设一个类vcform的实例工具并模式显示之。这只需如下代码:
void __fastcall tvcaptiondlg::execute()
{
vcform=new tvcform(application);
vcform->caption=caption;
vcform->showmodal();
delete vcform;
}
个中vcform为vcap.cpp中已声明的tvcform类范例的一个实例变量。相应地在vcapdlg.h里需插手一个execute要领的声明。
别的可以插手一些无关紧急的代码,如tvcaptiondlg的结构函数中插手成员变量的初始化语句等。至此整个控件的建造完成。完整的控件代码如下:
//vcapdlg.h
#ifndef vcapdlgh
#define vcapdlgh
#include
#include
#include
#include
class package tvcaptiondlg: public tcomponent
{
private:
ansistring fcaption;
protected:
public:
virtual __fastcall tvcaptiondlg(tcomponent *owner);
virtual void __fastcall execute();
__published:
__property ansistring caption={read=fcaption, write=fcaption};
};
#endif
//vcapdlg.cpp
#include
#pragma hdrstop
#include "vcapdlg.h"
#include "vcap.cpp"
#pragma package(smart_init)
static inline tvcaptiondlg * validctrcheck()
{
return new tvcaptiondlg(null);
}
namespace vcapdlg
{
void __fastcall package register()
{
tcomponentclass classes[1]={__classid(tvcaptiondlg)};
registercomponents("mailuo",classes,0);
}
}
__fastcall tvcaptiondlg::tvcaptiondlg(tcomponent * owner)
:tcomponent(owner)
{
fcaption="mailuo's sample";
}
void __fastcall tvcaptiondlg::execute()
{
vcform=new tvcform(application);
vcform->caption=caption;
vcform->showmodal();
delete vcform;
}
控件的安装不再赘述。
4 结语
本文旨在演示c++ builder的控件建造和动静处理惩罚、sdk等高级编程技能。以上代码全部在pwin98/c++ builder 3.0上通过调试。顺便指出,c++ builder的辅佐文档中的creating custom components讲控件建造讲得很是好,是进修编写控件的不行多得的好教程。但个中making a dialogbox a component一篇中有两处小小瑕疵:一是including the form unit中所讲用pragma link vcap.obj的要领是一个相对贫苦的要领,因为需要先将vcap.cpp放入一个无关项目中编译以生成obj文件(或是用呼吁行编译但要指定参数),不如本文一条#include"vcap.cpp"简朴。二是该文档中没有指出对这种本身生成的form,有一个限制就是必然要注释掉#pragma package(smart_init)行,不然安装时虽可生成包文件但控件不能被装上。它举的about对话框的例子中刚好没有这一句。而用ide发生的form一般都是这一句的。