首页 > 编程 > C++ > 正文

C++ Builder开发经验几则 

2019-09-06 23:34:02
字体:
来源:转载
供稿:网友

                        Borland C++ Builder 是Inprise公司继Delphi之后推出的又一激情之作,它使得C++的可视化编程真正成为了可能,这就为那些既不忍心放弃自己心爱的C++语言,又十分向往Windows平台下RAD编程的程序员们提供了一个近乎于完美的解决方案。其中坚版本Borland C++ Builder 3.0更是一优秀的全能型的开发工具。从开发功能上来说,其功能和现在如日中天的Microsoft的Visual C++相比不相上下,而从易用性方面来说,其易用性则要大大优于前者,正因为如此,所以Borland C++ Builder3.0在国内也逐渐得到了一些程序员的认同。但是Borland C++ Builder在国内的应用情况还远不及它的同门师兄Delphi一样普及,所以相对来说Borland C++ Builder3.0的参考资料也比较少。在这里笔者把自己这一年来使用Borland C++ Builder 3.0所总结出来的经验拿出几条来与大家共飨,只希望能对你在编程时有一定的帮助,让你少走一些弯路。

  1.全局变量的定义

  这个问题在标准的C/C++编程领域可以说根本不能算是一个问题,但是在Borland C++Builder3.0中却有些不同。当一个程序包含一个定义在独立头文件中的全局变量时,程序编译将会很顺利地通过,但是在程序链接时则会出现找不到全局变量目标模块的错误。这个问题曾令包括笔者在内的很多Borland C++ Builder 程序员感到困惑。直到前不久,我才找到了问题出现的原因和解决问题的方法。
  在Borland C++ Builder 3.0中对全局变量的定义采用了定义函数一样的方式,即全局变量不仅要有声明部分,还要有实例定义部分。只有这样定义过的全局变量在使用时才不会出错。
  以下举例说明,在程序中要定义一个全局整型变量i,它的声明部分写在Struct.H头文件中,如下所示:
  #ifndef StructH
  #define StructH
  #include //此行必须加上
  //------------------
  extern int i;//此处为全局变量i的声明
  //-------------------
  #endif
  而它的实例定义部分则应该写在Struct.cpp文件中,具体如下:
  //------------------
  #include “struct.h”
  #pragma package(smart_init)//此行必须加上。
  //------------------
  int i;//此处为全局变量i的实例定义
  //-------------------
  只有经过以上处理后,在程序中使用自定义全局变量时才不会出错。

  2.使同一个元件的Hint在StatusBar中与动态提示时有不同的显示内容

  这是我在阅读VCL源码时无意中发现的。虽然是一个小技巧,但是却很是实用,以下具体举例说明:
  ⑴:在Borland C++ Builder 3.0中新建一个项目,然后在窗口中加入一个Button元件和一个StatusBar元件,并在对象检器中将Button元件的ShowHint属性和StatusBar元件的SimplePanel属性值都置为true。
  ⑵:将Button元件的Hint属性置为“这是一个按钮的提示信息|这是在提示行上显示的信息”。
  ⑶:在窗口头文件的窗口类中定义一个函数原型如下:
  void __fastcall DisplayHint(TObject *Sender);
  然后在CPP文件中编写这个函数的代码如下:
  void __fastcall TForm1::DisplayHint(TObject *Sender)
  {
   StatusBar1->SimpleText=GetLongHint(Application->Hint);
   //应用程序在显示动态提示信息时,只会显示“|"以前的部分,而想要显示“|"以后的部分就只有通过调用GetLongHint函数来实现了。
  }
  ⑷:最后再在窗口的构造函数中加入以下一行代码:
   Application->OnHint=DisplayHint;
  ⑸:保存并编译项目后运行程序。
  这时你会发现,当你把鼠标移到Button上时,Button上所显示的动态提示信息和StatusBar上所显示的提示信息是不同的。
  下面附送一个为整个应用程序安装动态提示的程序段。它摘自我正在开发的一个应用程序:
  void __fastcall TForm1::DisplayHint(TObject *Sender)
  {
   // 先对已经存在窗口的Tag属性赋值,然后再通过判断当前窗口的方法来进行动态提示。
   //如果将建的窗口也需要动态提示的话,那么可先给窗口的Tag属性赋值,然后再将判断这个Tag属性的代码加入到下面的switch语句中。
   switch(Screen->ActiveForm->Tag)
   {
   case 1 :DFSStatusBar1->Panels->Items[1]->Text=GetLongHint(Application->Hint);break;
   case 5 :Form5->StatusBar1->SimpleText=GetLongHint(Application->Hint);break;
   case 8 :Form8->StatusBar1->SimpleText=GetLongHint(Application->Hint);break;
   case 11:Form11->StatusBar1->SimpleText=GetLongHint(Application->Hint);break;
   }
  }

  3.如何使DateToStr函数转换后的结果为长格式的日期字符串

  这是我在实际开发过程中遇到的一个问题。当我把TDateTimePicker元件的Date属性通过DateToStr函数转换后赋给一个Edit元件的Text属性时,Text得到的日期字符串数据只是以yy-mm-dd格式表示的短格式日期字符串数据。千年虫!眼看就要到二千年了,这怎么能行。本来我想用判断字符串的办法来解决这个问题,但是后来经过深入思考后发现该方法有很多的弊端。所以只有用一种通用的方法来解决问题了。在这种情况下,我查找了很多的资料,但是很不幸的是,我所查找的资料中没有一个提到解决这个问题的方法。无奈之下我打开了Borland C++ Builder 3.0的帮助文件,查看了所有关于日期的帮助信息。此招果然有效,在帮助文件中我找到了一组用于日期显示的外部变量,其中有两个AnsiString型变量,它们分别是ShortDateFormat和LongDateFormat。从它们的名称就可以看出,它们是用来存储短日期格式和长日期格式的。当时灵机一动,想通过修改这两个变量值的方法来解决问题,于是当即写了一段代码并运行之,才发现我的想法是可行的。具体代码段如下:
  void __fastcall TForm7::WriteLongDate(void)
  {
   AnsiString temp=ShortDateFormat;
   ShortDateFormat=LongDateFormat;
   Edit1->Text=DateToStr(DateTimePicker1->Date);
   ShortDateFormat=temp;
  }
  该程序段先保存短日期格式变量,然后将长日期格式变量赋给短日期格式变量,这样做后就使得当前的短日期格式和长日期格式的表示方式相同了。接下来使用日期转字符串函数DataToStr来将日期数据转换为AnsiString型数据,转换后的AnsiString就已经变成了长格式的日期数据。最后将先前保存的短日期格式变量恢复一下即可。就这样,问题被很好地解决了。同理,如果将长格式日期字符串转换为短格式日期数据,也可以使用上述方法,只不过要将上面赋值的顺序反过来即可。

  4.程序在编译链接时提示找不到某某文件应该怎么办?

  这是一个实际编程工作中经常遇到的问题。造成这个问题的原因很多,很难一一介绍,所以我在这里只向大家介绍几个造成这个问题的典型原因,以及其解决方法。希望能对你在解决这个问题时起到抛砖引玉的作用。
  ⑴:编译器找不到存在的文件。
  这个问题经常出现在编译添加新元件的应用程序时。其原因在于项目文件的搜索路径中并没有这个新元件所在路径的信息。这个问题解决起来比较简单,只需打开Project/Options菜单,在弹出的“选择”窗口中选择“Directories/Conditionals"标签页,然后将新元件的路径信息添加到包含路径(Include Path)和库文件路径(Library Path)中即可。
  ⑵:编译器要找不存在的文件情况之一
  也许你在编程中有过这样的遭遇,当你编译并链接一个新的程序时,编译程序竟然会告诉你,需要你已经卸载了的元件参加编译。不知道你当时的感觉怎么样,反正,当我还不知道这个问题解决办法的时候,它总给我一种死去的元件阴魂不散的感觉。其实造成这个问题的原因是由于新元件在安装时要将其库文件的信息添加到缺省项目文件中的“编译链接所需库文件”部分,而元件在卸载时又不能自动将其删除。这就使得以后所有新建的项目在编译链接时都需要使用这些库文件。为了解决这个问题,我们就只有对缺省项目文件进行编辑了。进入Borland C++ Builder 3.0的BIN目录下,你可以找到一个叫做DEFAULT.BPR的项目文件,这个文件就是缺省项目文件,即所有新建项目文件的父本。打开这个文件后,找到SPARELIBS段,将“死去元件”的“阴魂”Kill掉,即可永绝后患了。
  ⑶:编译器要找不存在的文件情况之二
  这种情况的出现还是与新元件的安装有关。当你在安装一些有源码的Delphi元件时,有可能遇到这样的情况:编译器告诉你找不到某某文件,使得挺好的一个元件就是无法使用。其实这个问题的出现多半是由于元件源码中的一些判断Delphi版本的预编译信息所造成的。由于Delphi的不同版本之间都有着一定的差异,所以很多Delphi元件为了增强自己的可移植性都在元件的源码中加入了判断Delphi版本的预编译信息。然而Borland C++ Builder 3.0的Delphi编译器对这些Delphi版本的信息支持并不是很好,这就造成了Borland C++ Builder 3.0在编译这些元件时通常是“胡子眉毛一把抓”,自然会出错(值得注意的是,在编译Delphi元件时的很多其它编译错误也是由于这个原因所造成的)。这时我们就只有使用手工删除这些不需要的预编译信息(一般为{$IFDEF中的内容)的方法来解决这个问题了。
  ⑷:编译器要找不存在的文件情况之三
  有一次一个从BC++转到Borland C++ Builder 3.0的朋友给我打电话问我,说他一时间动了怀旧的情绪,使用Borland C++ Builder 3.0编译了一个OWL程序。但是在编译时编译器却提示找不到OWL所需要的类库文件,问我这是为什么。当即我便回答了他,告诉他Borland C++ Builder 3.0的默认安装将不安装对OWL和MFC的支持库(这在Borland C++ Builder 3.0安装程序的安装类型界面中已经写得很明白了,只不过是英文的,他没有注意看罢了。)。如果需要这些东西的话,就只有在安装时选择定制安装或完全安装来安装对其的支持了。如果你也遇到了以上的问题,那么请你照我上面所说的话去做吧。

   5 动态调用窗体Form

   在缺省情况下,由File/NewForm生成添加入项目文件中的窗体都具有"AutoCreate"(自动创建)的特性。即只要程序运行,该窗体就存在于内存中了,不管当前它是否被调用。具有这种特性的窗体一般适用于窗体属性比较固定、经常被调用的情况。其优点是速度快,缺点是占用内存。在实际程序设计中,会遇见大量类似对话框功能的窗体,它们用于显示状态或输入信息,仅须在程序中调用一下,完成其功能就行了,无需常驻内存。这时可以通过选择Project/Options/Forms,将"Auto--Createforms"栏中相应的窗体,如Form1,用">"键移动到"Availableforms"栏中,并在程序需调用该窗体处,加入下列语句:
TForm1 *myform=newTForm1(this);
myform->ShowModal();
deletemyform;
 
  6 用Enter键控制焦点切换的方法

   在Windows环境下,要使一个控件取得焦点,可在该控件上用鼠标单击一下,或按Tab键将焦点移至该控件上。这种控制焦点切换的方法有时不符合用户的习惯。就图一而言,用户就希望用Enter键,控制焦点由Edit1切换到Edit2。要实现这样的功能需借助WinAPI函数SendMessage来完成。方法是:先设Form1的KeyPreview属性为true,然后在Form1的OnKeyPress事件中加入如下的代码。这样,用户就可以通过按Enter,键控制焦点按定义好的Taborder顺序来移动了!

void__fastcallTForm1::

FormKeyPress(TObject*Sender,char&Key)
{
if(Key==VK_RETURN)
  {
  SendMessage(this->Handle,WM_NEXTDLGCTL,0,0);
  Key=0;
  }
}


  7 为TStringGrid的文字加上颜色

TStringGrid是C++Builder提供给用户的一种字符网格控件。美中不足的是,它没有提供分别修改各单元字体颜色、大小的方法。其实要为TStringGrid实现这样功能,只需在程序中稍加处理就行了。方法是自定义一个二维数组cellbuf,它的下标与网格单元列行一一对应,用于存放各网格单元的颜色、文字等信息。

structCellStru

{

AnsiStringmsg; //文字信息

TColorcolor; //文字颜色

};

CellStrucellbuf[MAXCOL][MAXROW];


----初始化cellbuf后,再在字符网格控件StringGrid1的OnDrawCell响应事件中,加入如下的代码即可。

void__fastcallTForm1::StringGrid1DrawCell

(TObject*Sender,intCol,

intRow,TRect&Rect,TGridDrawStateState)

{

StringGrid1->Canvas->Font->

Color=cellbuf[Col][Row].color;

StringGrid1->Canvas->TextOut(Rect.Left+3,

Rect.Top+3,cellbuf[Col][Row].msg);

}


 8 软件封面的实现

----现代软件设计的流行做法是,在程序运行完成初始化之前,先调用一幅画面做为封面,通常是1/4屏幕大小,显示一下软件的名称、作者、版本等信息。要用C++Builder实现这样的功能,方法很简单:①自定义一窗体类TSplashForm,将其设置成"透明窗口",即BorderIcons下的所有选项均置成false,BorderStyle=bsNone,FormStyle=fsStayOnTop,Position=poScreenCenter;②在TSplashForm窗体上放置一TPanel(相当于图形的镜框);③在TPanel上放置一TImage控件,调入所需要的图形;④对WinMain函数稍加修改,加入如下所示代码即可。需要指出的是,这段代码通过函数FindWindow,搜索内存中是否有窗口标题为"Demo"应用程序存在,若存在,则退出程序的运行。该功能可防止程序的再次运行。在某些场合这样设计是必须的。

WINAPIWinMain(HINSTANCE,HINSTANCE,LPSTR,int)

{

try

{

if(FindWindow(NULL,"Demo")!=0)

{

Application->MessageBox

("程序已经运行!","警告",MB_ICONSTOP);

return0;

}



TSplashForm*splash=newTSplashForm(Application);

splash->Show();

splash->Update();



Application->Initialize();

Application->CreateForm(__classid(TForm1),&Form1);



splash->Close();

deletesplash;



Application->Run();

}

catch(Exception&exception)

{

Application->ShowException(&exception);

}

return0;

}


9 如何永久清除DBF中的已被删除的记录

----用table->Delete()删除的DBF记录,并没有真正从DBF数据库中被删除,而仅仅是做上了一个删除标记。如何实现类似dBase中的Pack命令的功能呢?请看下面的代码。

table->Close();

for(;;)

try

{

table->Exclusive=true;

table->Open();

break;

}

catch(...)

{

}



if(DbiPackTable(table->DBHandle,table->Handle,NULL,szDBASE,true)!=DBIERR_NONE)

Application->MessageBox("不能删除记录","错误",MB_ICONSTOP);

  最后再说一种最糟的情况。由于你的某种误操作,造成了某些编译器在编译时所需要文件被误删除。这时你唯一的解决方法就只有从Borland C++ Builder 3.0的光盘上将你所需要的文件直接拷贝过来。如果你的Borland C++ Builder 3.0只是一个安装版的话,那么你也就只好准备一杯清茶或一杯咖啡,在重新安装Borland C++ Builder 3.0时慢慢品味了。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选