如鹏网 大学生计算机学习社区

CowNew开源团队

http://www.cownew.com 邮件请联系 about521 at 163.com

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  363 随笔 :: 2 文章 :: 808 评论 :: 0 Trackbacks

#

博客开张了!
以后会把我的一些经验&一些想法发上来的,共同学习共同进步。主要是java&Delphi方面的吧,Delphi用了有将近三年了,用它做了两个项目(都是呼叫中心),做了N多的小软件,以后会陆续把他们贴上来的。现在在深圳一家比较有名的软件工作做开发,用的是java,公司的自主研发的MDA平台确实不错,自己偷偷的学了不少东西,也模仿着做了一些东西,其中的各种感想也会及时与大家交流的。
posted @ 2005-10-22 00:03 CowNew开源团队 阅读(306) | 评论 (1)编辑 收藏

 

问世间情为何“物”

―――情人节的礼物

发表于《电脑爱好者20032月刊》

又一个情人节到了,阿当的MM非要阿当写封情书送给她,说什么阿当平常工作忙,不浪漫,忽视她………阿当在强大的“武力”压迫之下只有屈服!可是已经“封笔”多年的阿当写情书真是个不小的难题呀,不过阿当就是阿当,他又打起了电脑的注意……

专为不同阶段,量身定做――唯美情书

初恋时期,情书是爱的鼓动力;热恋时期,情书是爱的奠基石;狂恋时期,情书是爱的醒脑剂;失恋时期,情书是起死回生的救命剂;成功时期,情书是爱的赞美诗(特别披露阿当之三“狗”阶段:先是“癞皮狗”,再来是“哈巴狗”,最终进入“狼狗”,目前阿当正在向着“狼狗”这一伟大目标前进^O^)。情书在不同时期自然内容也不大相同啦,软件内的上百封情书就是根据处于不同恋爱时期的人士而设计的。本软件可将写好的情书打包成EXEhtml,阿当觉得这个功能真的不错,阿当的MM对电脑勉强来说是个菜鸟,直接生长EXE文档,对她可方便多了,阿当也省去了很多解释时的口水了^O^

海量情书库――魔术情书

一万封以上的情书,还可随意扩充,真是海量呀,就凭这一点,阿当当然要重点推荐一下啦。软件更精选了一百多封中外经典情书,可以任您使用。比较有趣的,软件提供了发送匿名情书的功能,怎么样,给你暗恋中的MM一个惊喜吧,如果还搞不定的话,那……(阿当也没办法啦^O^

让你的情书“开口说话”――浪漫情书(LoveLetter

现在情书也可以“有声有色”了,怎么样?把“I LOVE YOU”“说”出来,MM不高兴才怪,来试试这个软件吧,它可是带有一个强大的情话库呀。

“情书精灵”是软件的一大特色,在第一次使用这个软件的时候,您可对它进行设置,对于到底有什么用,您快去试试吧!

它能及时地提醒您使用的方法。当您第一次使用“浪漫情书”时会出现一个初始化界面,您可以在其中输入对她的称呼、您的落款等信息。您还可以对它选择你喜欢的情话库!

两个人的世界,加密你的情书――心跳情书

该软件的情书书写方法包括:组合式书写,模板式书写,英文情书书写,脚本编程书写,电脑自动书写。如何?连英文的都可以写呀,国外的MM……它还能对情书加密,这个功能不错吧。这种东东除了心爱的MM外怎么可以给别人看呢。这个软件的另一个“神奇”之处大家猜一猜在哪儿?去看看帮助文件吧,里面的东东保证你非常受用!

恋爱物语之情书杀手BOY:内附“恋爱密技”,阿当很是受用,不可不看。

情书.net:网恋必备之利器,能够让人欢喜让人忧,不可不备。

情书生成器:阿当使用的第一个情书软件,全windows界面,一看就懂,写出的情书可以说是至幽至默。

posted @ 2005-10-21 23:54 CowNew开源团队 阅读(306) | 评论 (0)编辑 收藏


1 光宗耀祖
小菜同志近日迷恋上了编程,整天泡在电脑前忙的不亦乐乎。不过我发现昨天他好像一个晚上没有睡好,今天又神神秘秘的,还要我陪他去他祖父的墓地。到了墓地,他冲着他祖父的墓碑磕了三个响头,最里边不知在嘟囔着什么,好像在说“光宗耀祖”、“请爷爷在天之灵为我保佑”之类的话,而且还流着眼泪。我怕他出事,就问他怎么了?他想了半天,才说:“兄弟,咱哥俩从小长到大,这次小弟我求大哥您一件事。”。“什么事呀?你说吧!”,我说。“你编程水平不错,我又有商业头脑,咱们开个软件公司吧!”。“好呀,当然好了!不过咱们做什么东西呢?”,我问。他又一言不发,显得很激动,过了好久才说:“咱们回家再说吧!”。
到了他家,他急忙打开电脑,启动Delphi,打开了一个他做的Delphi源码,上面放着一个webbrowser控件和一个Edit输入框。他很激动地说:“大哥,我昨天才发现做一个浏览器也并不是很难,只用一个控件就能办到!所以,我想……我想成立一个软件公司,专门做网页浏览器!打败微软!为网景报仇!微软呀微软,我做的浏览器不比你的差!你用了这么多年开发IE,而我利用Delphi就能很快很好的做出来!我要挑战微软!祖辈呀!列祖列宗呀!我要光耀门楣了!我要打败微软了!”。这时他明显已经处于癫狂状态。我听到这个,差点把肚皮笑破,不过我还是忍住了笑,假装一本正经地说:“好呀!我也有个点子!”。说着,我把MediaPlayer控件装到了Delphi地组件面板上,然后用了不到一分钟,就“做”出了一个播放器,这时我发现他地眼都绿了。他狂叫着:“微软呀,微软!我也要在播放器领域与你分一倍羹!一分钟做一个播放器,你们的开发人员能做到嘛?兄弟,你任我们公司多媒体开发部的经理!”,我听了之后就晕了过去。过了半晌才醒过来。我刚睁开眼睛,就发现他正在摆弄着RealPlayer控件,并且用近乎神经质的声音大喊着:“RealNetworks公司!你们也完了!列祖列宗呀!……”,还没听完,我又晕了过去。
2 递归
小菜同志经过两个月的治疗,终于回到了正常人的状态。这两天又开始学习编程。有一天他向我请教怎么求一个数的阶乘,我随口答道:“用递归!”,然后我就出去做别的事情去了。过了好半天我才回来,发现他正在电脑前忙乎着。见我进来,他狂啸到:“什么递归呀!根本找不到递归这个控件!”,我再晕。
3 大彻大悟
小菜同志这两天功力见长,目光已经不局限于一门语言。受“聪明的程序员用C++理解Delphi”思想的指导,开始研习C++。一日,见小菜同志在电脑前冥思苦想,他见我进来了,马上装出大彻大悟状:“噢!这段C++代码写得不错呀!又规范,效率又高!不错!不错!”。我走上前去,看见屏幕上显示着:
“package mypic
import java.awt.*;
......”

 

 

posted @ 2005-10-21 23:51 CowNew开源团队 阅读(408) | 评论 (0)编辑 收藏

 

八讲的内容这么快就讲完了,通过这八讲相信大家对于Delphi控件开发有了大体的了解。但是仅仅了解还不够,要自己去发现:我做的程序中有没有可以提炼成控件的东西?一旦发现有可以提炼成控件的东西,就要尽力将它抽象化成控件。这样将能成倍甚至成十倍的提高系统的开发进度。
  这篇文章其实是我去年写的,当时发在了我的个人网站上,但是由于空间到期了,我就一直没有理他。昨天注册了一个blog,所以想把这些东西再放出来,希望能给需要它的朋友带来一点帮助。

  我在实际的开发系统中开发了很多实用的控件,这些组件列表如下:
1、BigStringContainer:可以在程序中以资源的形式嵌入大尺寸的字符串,比如一篇文章,并且通过一个属性就读取出来。
2、BrowseComputerDlg:选择局域网中的所有计算机的对话框。
3、CheckTreeView:节点前带复选框的树控件。
4、DataSet2Excel:将DataSet中的数据导出为Excel文件。
5、DataSetHTMLProducer:将DataSet中的数据导出为HTML。
6、DBDateTimepk:可以做数据绑定的日期、时间选择控件。
7、DBLabelEdit:带Label标签的文本框,可以做数据绑定。
8、DBOperatTool:带有"添加""删除""修改"“确认”“取消”的按钮的数据库操纵工具面板。其中【删除】按钮点击的时候还会弹出一个带“下次不再显示此对话框”的确认对话框。
9、DBSelectDlgEdit:此控件可以用来选择数据库中已有的数据,是弹出对话框选择。
10、ExcelCreator:可以生成Excel文件,在没有安装Excel的机器上也无可以运行。
11、IconContainer:可以在程序中以资源的形式嵌入图标,并且通过一个属性就读取出来。
12、LincoClock:表盘时钟控件。
13、PopupMsg:像QQ一样可以在右下角弹出消息框,并且会超时自动缩回。
14、qqpanel:QQ风格的面板。
15、selectdirdlg:选择文件夹目录的对话框控件。
16、switch:长得像按钮开关一样的控件。
17、sysMenu:可以将一个PopMenu设置到窗口的系统菜单中显示,和“最大化”、“最小化”那些菜单在一起。
18、trayicon:原创的系统拖盘图标控件。比市面上流行的TrayIcon更小巧,功能一点都不弱,安装也更加简单。
19、urllabel:长得像超链接的Label,用户点击label的时候自动打开网址。
20、URLRichEdit:自动识别超链接并添加下划线的URLRichEdit控件。
21、窗口设计器,可以实现类似于Delphi那样的控件拖放效果。

以上控件、源码每份30元(不带售后服务,只提供源码),如果需要提供售后服务每份再加30元,售后服务只包括安装、使用指导、修改Bug等售后服务,不包括增加新功能、修改功能、讲解代码等,如果需要安装、使用指导、修改Bug等售后服务,费用面议。

以上源码打包购买只需要300元,另加200元提供售后服务(不包括增加新功能、修改功能、讲解代码等)。

以上代码是个人开发,从未上传到网上,因此不用费劲去网上找泄露版,找不到的。这么长的《Delphi控件开发浅入深出》系列文章已经免费提供了,希望大家能够谅解。上面的源码、控件都是我的心血之作,我免费提供是我的无私之心,收费提供是我的本分,因此请不要说黑不黑的问题,花几百元买一件衣服怎么从来没有骂过服装商黑呢?我也要养家。

购买请到CowNew的淘宝网店: http://item.taobao.com/auction/item_detail.jhtml?x_id=0db2&item_id=2ee8dc9c5459bd770f598989568ce745

比如需要购买DataSet2Excel和PopupMsg的源码,并且需要购买PopupMsg的售后,只要拍下3件“CowNew技术服务”即可,拍下后我们将尽快向你提供源码和售后服务.我们还支持手机充值卡支付,详见http://www.rupeng.com/index.php/action-channel-name-pengbishop

posted @ 2005-10-21 23:49 CowNew开源团队 阅读(2815) | 评论 (26)编辑 收藏

 

八、数据敏感控件的制作。

Delphi的一大亮点就是它的数据库开发能力。而数据敏感组件则在这中间起着很重要的作用。在DelphiData Control页面下的控件都是用于显示和编辑数据库中的数据的。相信大家已经体会到数据敏感控件的好处了。我们这一节就给大家演示一下数据敏感控件的开发方法。

需要提醒大家的是,不像其他体系的控件,数据敏感控件并没有一个统一的基类,只要是从TwinControl类或其子类派生就可以,数据敏感控件的特殊之处就在于我们下面提到的数据连接。

相信用Delphi开发过数据库的人一定对delphi中没有一个日期数据敏感控件而恼火。每次都要我们自己处理数据的更新与显示。所以我们就来开发一个DBDateTimePicker控件。

新建一个控件,从TdateTimePicker派生,源代码如下:

{*******************************************************}

{       Linco TDBDateTimePicker

{       mail me: about521@163.com                       }

{*******************************************************}

unit DBDateTimePicker;

interface

uses

  SysUtils, Classes, Controls, ComCtrls, DBCtrls, Messages, DB;

type

  TDBDateTimePicker = class(TDateTimePicker)

  private

    FDataLink: TFieldDataLink;

    procedure CMGetDataLink(var Msg: TMessage);message CM_GETDATALINK;

    procedure DataChange(Sender: TObject);

    procedure EditingChange(Sender: TObject);

    procedure FSetDataField(AValue: string);

    procedure FSetDataSource(AValue: TDataSource);

    procedure FSetReadOnly(AValue: Boolean);

    procedure ShowData;

    procedure UpdateData(Sender: TObject);

    function FGetDataField: string;

    function FGetDataSource: TDataSource;

    function FGetField: TField;

    function FGetReadOnly: Boolean;

  protected

    procedure Change;override;

    procedure Notification(AComponent: TComponent;Operation: TOperation);override;

  public

    constructor Create(AOwner: TComponent); override;

    destructor Destroy; override;

    property Field: TField read FGetField;

  published

    property DataField: string read FGetDataField write FSetDataField;

    property DataSource: TDataSource read FGetDataSource write FSetDataSource;

    property ReadOnly: Boolean read  FGetReadOnly write FSetReadOnly;

  end;

procedure Register;

implementation

uses  Variants;

constructor TDBDateTimePicker.Create(AOwner: TComponent);

begin

  inherited Create(AOwner);

  FDataLink := TFieldDataLink.Create;

  FDataLink.OnDataChange := DataChange;

  FDataLink.Control := self;

  FDataLink.OnEditingChange := EditingChange;

  FDataLink.OnUpdateData := UpdateData;

  self.DateTime := Now();

end;

 

destructor TDBDateTimePicker.Destroy;

begin

  FDataLink.Free;

  inherited;

end;

 

procedure TDBDateTimePicker.CMGetDataLink(var Msg: TMessage);

begin

  Msg.Result := Integer(FDataLink);

end;

 

procedure TDBDateTimePicker.DataChange(Sender: TObject);

begin

  if Field<>nil then

    if Field.Value = null then

      if (DataSource.DataSet.State = dsEdit)

        or (DataSource.DataSet.State = dsInsert) then

        Field.AsDateTime := Now();

  ShowData;

end;

 

procedure TDBDateTimePicker.EditingChange(Sender: TObject);

begin

  if (DataSource <> nil) and (DataField <> '') then

    FDataLink.Edit;

end;

 

procedure TDBDateTimePicker.FSetDataField(AValue: string);

begin

  FDataLink.FieldName := AValue;

end;

 

procedure TDBDateTimePicker.FSetReadOnly(AValue: Boolean);

begin

  FDataLink.ReadOnly := AValue;

end;

 

procedure TDBDateTimePicker.ShowData;

begin

  if (DataSource <> nil) and (DataField <> '') and(Field<>nil)then

  begin

    case Kind of

    dtkDate: if Field.AsString <> '' then

               self.Date := Field.AsDateTime

             else

               self.Date := Now();

    dtkTime: if Field.AsString <> '' then

               self.Time := Field.AsDateTime

             else

               self.Time := Now(); 

    else

      self.DateTime := Now();

    end;

  end;

end;

 

procedure TDBDateTimePicker.FSetDataSource(AValue: TDataSource);

begin

  FDataLink.DataSource := AValue;

  if AValue <> nil then

    AValue.FreeNotification(self);

end;

 

procedure TDBDateTimePicker.Change;

begin

  if (DataSource <> nil) and (DataField <> '') then

  begin

    FDataLink.Edit;

    Field.Value := self.Text;

  end;

  inherited Change;

end;

 

procedure TDBDateTimePicker.Notification(AComponent: TComponent;Operation: TOperation);

begin

  if (Operation = opRemove) and (FDataLink <> nil) and

      (AComponent = DataSource) then

    DataSource := nil;

end;

 

procedure TDBDateTimePicker.UpdateData(Sender: TObject);

var

  t: TFieldType;

begin

  if (DataSource <> nil) and (DataField <> '') then

  begin

    t := FDataLink.Field.DataType;

    case t of

    ftTime: FDataLink.Field.AsDateTime := self.Time;

    ftDate: FDataLink.Field.AsDateTime := self.Date;

    ftDateTime: FDataLink.Field.AsDateTime := self.DateTime;

    end;

  end;

end;

 

function TDBDateTimePicker.FGetDataField: string;

begin

  result := FDataLink.FieldName;

end;

 

function TDBDateTimePicker.FGetDataSource: TDataSource;

begin

  result := FDataLink.DataSource;

end;

 

function TDBDateTimePicker.FGetField: TField;

begin

  result := FDataLink.Field;

end;

 

function TDBDateTimePicker.FGetReadOnly: Boolean;

begin

  result := FDataLink.ReadOnly;

end;

procedure Register;

begin

  RegisterComponents('Linco', [TDBDateTimePicker]);

end;

end.

 

谈到开发数据敏感控件就不得不说数据连接(DataLink),数据连接有很多种,开发数据敏感控件最常用到的就是字段数据连接(TFieldDataLink)。数据连接是联系数据敏感控件和数据库的通道。在数据敏感控件中就是凭借着数据连接来处理数据的更新和显示的。从后边我们的描述中您将更加能体会到,正是数据连接把数据在数据库中的表示反映到用户界面中,也是数据连接把数据从用户界面更新到数据库中。数据连接就是一个“大媒人”(这其实是设计模式中Mediator中介者模式的典型应用)。

既然字段数据连接这么重要,我们就先来系统的介绍一下它吧!TfieldDataLink闪亮登场!!!

TfieldDataLink的属性:

1)、property CanModify: Boolean;表示这个字段是不是只读的。

2)、property Control: TComponent;指定这个字段数据连接被连接到哪个数据敏感控件。因为字段数据连接要把它的状态改变通知包含它的数据敏感控件。

3)、property Editing: Boolean; 表示这个字段是不是可以被编辑。

4)、property Field: TField;表示这个字段数据连接连接的字段。

5)、property Active: Boolean;表示字段数据连接连接的数据集是否处于激活状态。

6)、property FieldName: String;字段名。

7)、property DataSource: TDataSource;表示它连接的数据源。

8)、property DataSet: TDataSet;表示它负责维护的数据集。

方法:

1)、function Edit: Boolean;尝试设置字段为编辑状态。如果设置成功则返回True,反之返回False;

事件:

1)、property OnActiveChange: TNotifyEvent;Active属性变化的时候发生此事件。

2)、property OnDataChange: TNotifyEvent;当数据集发生变化的时候发生。

3)、property OnEditingChange: TNotifyEvent;当数据源从编辑状态变为其他状态或从其他状态变为编辑状态的时候发生。

4)、property OnUpdateData: TNotifyEvent;当向数据库提交对数据库的修改时发生此事件。

代码分析:

1)、做为一个数据敏感控件,它首先要实现的功能就是允许用户将此控件连接到一个数据源(DataSource)。我们还要用户能选择这个控件绑定到哪个字段。

将控件连接到一个数据源,而数据源又是一个控件,所以这就是一个关联控件属性方法的应用。FsetDataSourceFDataLink.DataSource := AValue;这句代码是最重要的。就像我们前面讲到的数据连接就是一个在数据源和数据敏感控件之间的媒人,所以数据源(DataSource)要告诉媒人是它要被连接到数据敏感控件,而不是别人,告诉媒人的唯一方法就是设定媒人的DataSource为自己(即要绑定的数据源)。因为我们的显示日期的控件只能显示一个字段,还要告诉媒人自己的哪个字段要绑定到数据敏感控件,这个通过数据敏感控件的FieldName属性来进行。即:

procedure TDBDateTimePicker.FSetDataField(AValue: string);

begin

  FDataLink.FieldName := AValue;

end;

2)、我们还可以为控件增加一个Field属性,这样用户就可以通过DBDateTimePicker.Field.AsString = ‘ok’;这样的方式对字段进行操作了。当然了,这最终还是通过数据连接的Field属性来进行的。

3)、由于VCL内部通信机制的要求,数据敏感控件要响应CM_GETDATALINK事件。只要在事件相应函数里边把消息的Result域赋值为DataLink的地址就可以了。也就是:

procedure TDBDateTimePicker.CMGetDataLink(var Msg: TMessage);

begin

  Msg.Result := Integer(FDataLink);

end;

4)、就像DBEdit一样,在用户通过改变控件中的日期时,应该能将改动保存到数据库字段中。我们覆盖控件的调度方法Change(在显示的数据变化时被调用)以将变化保存到数据库中。

procedure TDBDateTimePicker.Change;

begin

  if (DataSource <> nil) and (DataField <> '') then

  begin

    FDataLink.Edit;//设置数据连接为编辑状态,由这个媒人将数据库绑定的字//段设置为编辑状态

    Field.Value := self.Text;//设定数据字段的值

  end;

  inherited Change;

end;

5)、回头再来看看构造函数吧!

    FDataLink.OnDataChange := DataChange;

   FDataLink.OnEditingChange := EditingChange;

  FDataLink.OnUpdateData := UpdateData;

  FDataLink.Control := self;

前三句是设定响应数据连接事件处理句柄,正是这三句把数据库中的数据与用户界面联系了起来。关于这三个事件处理句柄的实现请参加源代码,这里就不多说了。

思考题:

1、做一个显示是/否的数据敏感控件,当这个控件与一个布尔类型的字段连接的时候,如果字段的值是0则显示“否”,如果字段的值是1则显示“是”;同时可以接受用户的修改,当用户在控件上单击一次鼠标,布尔值就翻转一次。

posted @ 2005-10-21 23:39 CowNew开源团队 阅读(2938) | 评论 (4)编辑 收藏

 

对话框控件的制作

Delphi中有很多对话框组件,例如TopenDialogTfontDialog等。这些控件的特点就是虽然是不可视控件,但是在运行时都有一个可视化的效果,比如TopenDialog的可视化效果就是一个打开对话框。我们这次将开发一个日期对话框控件,当我们调用控件的Execute方法(不一定非要使用Execute方法,不过大部分对话框控件都是使用这个方法,我们也就按照惯例来了)时,就会弹出一个可以选择日期的对话框,我们选择一个日期后,点击“确定”则Execute返回True,点击“取消”则Execute返回False。我们可以读取Date属性来得到用户选择的日期,也可以修改此属性来改变对话框的初始日期。

1、新建一个对话框。在对话框窗体上放置一个TmonthCalendar组件,命名为Cal,窗体名称改为FormDate。在窗体上放置两个按钮,一个按钮的Caption为“确定(&O)”,ModalResultmrOk,一个按钮的Caption为“取消(&C)”,ModalResultmrCancel。设计好的窗体如下图所示:

delphicomdev7-1.jpg

2、为窗体添加两个Public访问级的方法:

    function GetSelDate: TDate;

    procedure SetInitDate(AValue: TDate);

代码如下:

function TFormDate.GetSelDate: TDate;

begin

  result := cal.Date;

end;

 

procedure TFormDate.SetInitDate(AValue: TDate);

begin

  cal.Date := AValue;

end;

3、新建一个控件,派生自Tcomponent

代码如下:

unit DateDialog;

 

interface

 

uses

  SysUtils, Classes, Controls, frmDlg;

 

type

  TDateDialog = class(TComponent)

  private

    FDlg: TFormDate;

    function GetDate: TDate;

    procedure SetDate(AValue: TDate);

  protected

  public

    constructor Create(AOwner: TComponent);override;

    function Execute: Boolean;

  published

    property Date: TDate read GetDate write SetDate;

  end;

 

procedure Register;

 

implementation

procedure Register;

begin

  RegisterComponents('Linco', [TDateDialog]);

end;

constructor TDateDialog.Create(AOwner: TComponent);

begin

  inherited Create(AOwner);

  FDlg := TFormDate.Create(self);

end;

 

function TDateDialog.Execute: Boolean;

begin

  result := (FDlg.ShowModal = mrOK);

end;

 

function TDateDialog.GetDate: TDate;

begin

  result := FDlg.GetSelDate;

end;

 

procedure TDateDialog.SetDate(AValue: TDate);

begin

  FDlg.SetInitDate(AValue);

end;

end.

代码比较简单就不多解释了。

思考题:

1、做一个模仿TcolorDialog的对话框控件。

posted @ 2005-10-21 23:37 CowNew开源团队 阅读(1458) | 评论 (0)编辑 收藏

 

六、控件手拉手――控件关联的实现

控件的关联在Delphi中也是很常见的,我们可以设定一个控件的某个属性指向另一个控件。比如我们在窗体上放上Tedit,TpopupMenu两个控件,然后设定TeditPopupMenu属性为TpopupMenu控件,运行后在Tedit点击右键就会弹出刚才设定的那个TpopupMenu菜单,也就是说Tedit,TpopupMenu联手完成了任务。再比如TDBEdit控件的DataSource属性就可以指向一个TdataSource控件,这样就可以在TDBEdit控件中显示TdataSource输出的某个字段的值了。

  下面我们将写一个简单的实现控件关联的控件。这个控件派生于Tedit,它可以与一个Tlabel控件关联,在控件的编辑框中输入文字时,与它关联的Tlabel控件的文字将随着它而变化。代码如下:

unit MyEdit;

interface

uses

  SysUtils, Classes, Controls, StdCtrls;

type

  TMyEdit = class(TEdit)

  private

    FLinkLabel: TLabel;

    procedure FSetLinkLabel(AValue: TLabel);

  protected

    procedure Notification(AComponent: TComponent;Operation: TOperation);

          override;

    procedure Change;override;

  public

  published

    property LinkLabel: TLabel read FLinkLabel write FSetLinkLabel;

  end;

 

procedure Register;

 

implementation

procedure Register;

begin

  RegisterComponents('Linco', [TMyEdit]);

end;

procedure TMyEdit.Change;

begin

  inherited;

  if LinkLabel <> nil then

    LinkLabel.Caption := Text;

end;

 

procedure TMyEdit.FSetLinkLabel(AValue: TLabel);

begin

  FLinkLabel := AValue;

  if AValue <> nil then

    FLinkLabel.FreeNotification(self);

end;

 

procedure TMyEdit.Notification(AComponent: TComponent;

  Operation: TOperation);

begin

  inherited;

  if (Operation = opRemove) and (AComponent = LinkLabel) then

    LinkLabel := nil;

end;

end.

代码解释:

1)、我们只要将控件的任意一个属性的类型设定为另外一个控件的类名称,那么我们就可以在控件的Object Inspector中将这个属性指向那个控件(或那个控件的派生控件)的一个实例。比如本例中我们增加了LinkLabel属性,它的类型为 Tlabel,所以我们就可以把LinkLabel属性指向一个标签控件。

2)、请注意FsetLinkLabel中的这段代码:

 if AValue <> nil then

FLinkLabel.FreeNotification(self);

如果我们将控件关联属性指向了一个控件,可是后来又将被指向的控件删除了,那么我们的控件关联属性是不会自动删除的,这样就会造成控件关联属性指向的控件不存在的现象。我们必须自动感知被关联控件的删除并重新设定控件关联属性为不指向任何控件,这样就避免了错误的发生。

FLinkLabel.FreeNotification(self);的作用就是这样的。它调用控件的FreeNotification方法(在Tcomponent中定义)向被指向的控件注册一个“消息”,当被指向控件被删除时,会向所有向他注册的控件发送一个它被删除的消息,此时向他注册的控件就会触发Notification方法,这样我们就可以自动感知被指向控件的状态了。这是设计模式中Observer(观察者)模式的典型应用。

既然向他注册的控件就会触发Notification方法,我们就覆盖父类的Notification方法,写出如下的代码:

  if (Operation = opRemove) and (AComponent = LinkLabel) then

    LinkLabel := nil;

这句话的意思是:如果控件被删除并且被删除的控件(因为我们的控件可能向多个控件注册了消息)是LinkLabel,那么我们就设定LinkLabel属性不指向任何控件。

3)覆盖父类的Change调度方法。在此方法里为连接的LinkLabelCaption赋值就达到我们的目的了。

思考题:

1、做一个Label控件,给它增加一个DataSource属性,该属性可以指向一个TdataSource类型的控件,它有一个GetRecordCount方法。当调用此方法时,就在Label控件中显示这个DataSource对应的数据集中的记录的条数。

posted @ 2005-10-21 23:34 CowNew开源团队 阅读(2138) | 评论 (0)编辑 收藏

 

五、复合控件

复合控件是Delphi控件中非常重要的一种控件,复合控件就是将两个或两个以上的控件重新组合成一个新的控件。例如TspinEditTlabeledEditTDBNavigator等就是复合控件,TDBNavigator其实就是在一个Panel放上若干个Button而已。制作一个复合控件时,我们一般从TwinControl派生控件。

我们这次做的控件是拥有一个Edit编辑框和一个Button按钮的复合控件,在用户在编辑框中输入文字的过程中,Button将随时显示编辑框中文字的长度。我们把控件的源码先展示给大家。

unit EditButton;

interface

uses

  SysUtils, Classes, Controls, StdCtrls, Messages;

type

  TEditButton = class(TWinControl)

  private

    FEdit: TEdit;

    FButton: TButton;

    FText: string;

    procedure FSetText(AValue: string);

    procedure OnEditChange(Sender: TObject);

  protected

    procedure WMSize(var Msg: TMessage);message WM_SIZE;

  public

    constructor Create(AOwner: TComponent);override;

    destructor Destroy;override;

  published

    property Text: string read FText write FSetText;

  end;

 

procedure Register;

implementation

 

procedure Register;

begin

  RegisterComponents('Linco', [TEditButton]);

end;

 

constructor TEditButton.Create(AOwner: TComponent);

begin

  inherited;

  FEdit := TEdit.Create(nil);

  FEdit.Parent := self;

  FEdit.Top := 0;

  FEdit.Left := 0;

  FEdit.Height := Height;

  FEdit.Width := Width div 2;

  FEdit.OnChange := OnEditChange;

  FButton := TButton.Create(nil);

  FButton.Parent := self;

  FButton.Top := 0;

  FButton.Left := Width div 2;

  FButton.Height := Height;

  FButton.Width := Width div 2;

end;

 

destructor TEditButton.Destroy;

begin

  FEdit.Free;

  FButton.Free;

  inherited;

end;

 

procedure TEditButton.FSetText(AValue: string);

begin

  FEdit.Text := AValue;

end;

 

procedure TEditButton.OnEditChange(Sender: TObject);

begin

  FButton.Caption := IntToStr(Length(FEdit.Text));

end;

 

procedure TEditButton.WMSize(var Msg: TMessage);

begin

  FEdit.Height := Height;

  FEdit.Width := Width div 2;

  FButton.Left := Width div 2;

  FButton.Height := Height;

  FButton.Width := Width div 2;

end;

end.

代码解释:

1)、我们首先定义了两个变量  

    FEdit: TEdit;

    FButton: TButton;

  分别代表复合控件中的文字编辑框和按钮。

2)所谓复合控件说简单一点就是在一个共同的基板上将组成复合控件的各个控件(可以叫做子控件)画出来。所以我们在构造函数中建立各个子控件,然后分别设定它们的位置等属性。

以文字编辑框为例:

FEdit := TEdit.Create(nil);

的作用是建立编辑框控件。如果Create的参数指定为nil,则子控件在设计状态是可以响应用户的操作的;而如果设定为self(即设定子控件的父控件为基板),则子控件在设计时时不可响应用户操作的,如果设定为self则析构函数中就不用Fedit.Free来销毁对象了,对象会自动销毁。

  FEdit.Parent := self;的作用是设定子控件的父控件,如果没有这一句则控件是无法显示的。

  FEdit.Top := 0;

  FEdit.Left := 0;

  FEdit.Height := Height;

  FEdit.Width := Width div 2;

这四句是设定控件在基板上的相对位置的,这里的Top,Left不是相对于窗体的,而是相对于基板的。

  FEdit.OnChange := OnEditChange;

则是设定编辑框控件的OnChange(文字改变事件)的处理句柄为OnEditChange

(1)    用户有可能在设计时或运行时通过代码改变控件的大小,这时控件中子控件的顺序就会变得乱七八糟,所以需要相应控件的WM_SIZE事件(控件大小发生变化的事件)重新设定子控件的位置,大小等。函数WMSize的作用就是这样的。

安装控件后发现控件已经可以正确运行了,但是还有一个问题,就是这个控件没有了Onclick,Onchange等必须的属性。我们只要为控件增加事件处理句柄属性,然后把事件处理句柄属性的读写方法都指向子控件的事件处理句柄属性即可。例如我们为控件增加OnClick事件,这个事件发生在用户单击按钮时,我么只要在Pulished部分增加如下代码:

property  OnClick:  TnotifyEvent read GetOnClick write SetOnClick

Private中增加如下方法声明:

function GetOnclick: TnotifyEvent;

procedure SetOnclick(AValue: TnotifyEvent);

这两个方法的实现分别为:

function  TeditButton. GetOnclick: TnotifyEvent;

begin

  result := Fbutton.Onclick;

end;

procedure TeditButton. SetOnclick(AValue: TnotifyEvent);

begin

  Fbutton.OnClick := Avalue;

end;

思考题:

1、做一个模仿播放器中的操作按钮的复合控件,控件由三个按钮组成,分别是“播放”、“暂停”、“停止”,请按照正常的逻辑关系,处理这三个按钮的可用/不可用关系。(提示:可以参考TDBNavigator的源代码)

posted @ 2005-10-21 23:32 CowNew开源团队 阅读(3250) | 评论 (0)编辑 收藏

 

四、对特定字符串敏感的Edit控件

我们这个控件将演示控件的自定义事件的书写。这个控件有一个类型为stringSensitiveText属性,当用户在输入框中输入的文字为InvalidText时就会触发OnSensitiveText事件。按照惯例,我先把源码展示给大家:

unit TextSenseEdit;

 

interface

 

uses

  SysUtils, Classes, Controls, StdCtrls;

type

  TSensitiveTextEvent = procedure(AText: string) of object;//方法指针

  TTextSenseEdit = class(TEdit)

  private

    FSensitiveText: string;

    FOnSensitiveText: TSensitiveTextEvent;

    procedure SetSensitiveText(AValue: string);

  protected

    procedure Change;override;

  public

  published

    property SensitiveText: string read FSensitiveText write SetSensitiveText;

    property OnSensitiveText: TSensitiveTextEvent read FOnSensitiveText write FOnSensitiveText;

  end;

 

procedure Register;

 

implementation

 

procedure Register;

begin

  RegisterComponents('Linco', [TTextSenseEdit]);

end;

procedure TTextSenseEdit.Change;

begin

  inherited;

  if Text = SensitiveText then

    if Assigned(OnSensitiveText) then

      OnSensitiveText(Text);

end;

 

procedure TTextSenseEdit.SetSensitiveText(AValue: string);

begin

  FSensitiveText := AValue;

end;

end.

代码解释:

1)、SensitiveText属性的添加方法大家已经熟悉了,这里不多解释。

2)、正如大家猜测的,Change方法正是编辑框文字发生变化时的调度方法,它将引起OnChange事件。我们可以在这个方法中监控编辑框文字发生的变化,当文字等于SensitiveText就触发OnSensitiveText事件(具体的实现方法在后边解释)。

3)、Delphi中的控件的事件机制是通过方法指针来实现的。声明方法指针的格式为:

方法指针名称 = procedure(参数列表) of object;

声明事件属性的方法与声明普通属性的方法相同。在我们这个例子中,我们首先声明一个FOnSensitiveText: TSensitiveTextEvent;私有变量,然后property OnSensitiveText: TSensitiveTextEvent read FOnSensitiveText write FOnSensitiveText; 声明事件属性。这样注册控件后,当用户把控件放到窗体中后,就会在Object InspectorEvnets页中出现OnSensitiveText事件,我们就可以像使用其他事件一样使用这个事件了。

  但是我们现在只是声明了一个事件属性,并没有书写任何代码来激发这个事件。我们应该在合适的时候激发此事件,显而易见我们应该在Change方法中激发此事件:

procedure TTextSenseEdit.Change;

begin

  inherited;

  if Text = SensitiveText then

    if Assigned(OnSensitiveText) then

      OnSensitiveText(Text);

end;

if Text = SensitiveText时就判断控件使用者是否为OnSetSensitiveText写代码了(准确的说是是否为OnSetSensitiveText事件句柄赋值了),如果写代码了则调用OnSetSensitiveText(Text);来激发OnSetSensitiveText事件,并把控件的Text传递给方法的Avalue参数。正如“方法指针”这个名字一样,被声明为方法指针类型的变量可以当作方法使用,用来激发事件。VCL已经为我们预定义了一些常用的事件句柄,我们直接拿来使用:TnotifyEventTmouseEventTmouseMoveEventTkeyPressEvent等,具体可以参考VCL源码。

思考题:

1、做一个支持累加运算的文本编辑框控件,用户可以在编辑框中输入正整数。当用户按回车时,如果编辑框中输入的不是正整数(为负数、小数或一般字符串)则触发控件的OnError事件;如果输入的是正整数,则开始计算从1到用户输入的那个正整数中所有整数的和(用123+……这种累加的办法实现,不要用(1+n)*n/2这种直接计算的方法),并且在计算工程中如果发现计算的中间结果位数是5,则触发OnTailFive事件。

posted @ 2005-10-21 23:30 CowNew开源团队 阅读(1587) | 评论 (0)编辑 收藏

 

三、开关控件TlincoSwitch

用过Delphi1(好古老的东东呀!)的人相信都记得这个开关控件 r_delphicomdev3-1.jpg,不知道当初Borland为什么把这么一个在开发普通应用程序中应用不到的工控控件放到Delphi中,而且在Delphi2及其以后的版本中再也没有见过它的身影。让我们怀着怀旧的心情把这位“开国元老”请出来吧!

1、建立位图资源文件:

Image Editor建立一个Res文件,并在文件中分别建立下面两个位图r_delphicomdev3-1.jpgr_delphicomdev3-2.jpg,并分别命名为SWITCHONSWITCHOFF。保存此Res到控件单元所在目录下。

2、写控件代码。

unit LincoSwitch;

interface

uses

  SysUtils, Classes, Controls, Graphics, Windows;

 

type

  TLincoSwitch = class(TCustomControl)

  private

    FIsOn: Boolean;

    FPicOn: Graphics.TBitmap;

    FPicOff: Graphics.TBitmap;

    procedure FSetIsOn(AValue: Boolean);

  protected

    procedure Click;override;

    procedure Paint;override;

  public

    constructor Create(AOwner: TComponent);override;

    destructor Destroy;override;

  published

    property IsOn: Boolean read FIsOn write FSetIsOn;

    property OnClick;

    property OnKeyDown;

    property OnKeyPress;

    property OnKeyUp;

    property OnCanResize;

    property OnDblClick;

    property OnMouseDown;

    property OnMouseMove;

    property OnMouseUp;

    property OnMouseWheel;

    property OnResize;

  end;

 

procedure Register;

implementation

{$R *.res}

procedure LoadBitmapFromRes(ABitmapId: string; ABitmap: Graphics.TBitmap);

begin

  ABitmap.LoadFromResourceName(hInstance, ABitmapId);//从资源文件中读取位图

end;

 

constructor TLincoSwitch.Create(AOwner: TComponent);

begin

  inherited Create(AOwner);

  FPicOn := Graphics.TBitmap.Create;

  FPicOff := Graphics.TBitmap.Create;

  LoadBitmapFromRes('SWITCHON', FPicOn);

  LoadBitmapFromRes('SWITCHOFF', FPicOff);

  Invalidate;

end;

 

destructor TLincoSwitch.Destroy;

begin

  FPicOn.Free;

  FPicOff.Free;

  inherited;

end;

 

procedure TLincoSwitch.Click;

begin

  IsOn := not IsOn;//改变按钮的状态

  Invalidate;

  inherited;

end;

 

procedure TLincoSwitch.Paint;

begin

//画开关图案 

if IsOn then

   

      StretchBlt(Canvas.Handle, 0, 0, self.Width, self.Height, FPicOn.Canvas.Handle,

             0, 0, FPicOn.Width, FPicOn.Height,SRCCOPY)

  else

      StretchBlt(Canvas.Handle, 0, 0, self.Width, self.Height, FPicOff.Canvas.Handle,

             0, 0, FPicOff.Width, FPicOff.Height,SRCCOPY);

end;

 

 

procedure TLincoSwitch.FSetIsOn(AValue: Boolean);

begin

  FIson := AValue;

  Invalidate;

end;

 

procedure Register;

begin

  RegisterComponents('Linco', [TLincoSwitch]);

end;

end.

3、代码分析

  1)、因为我们要在控件表面上将按钮的图案画出来,所以我们选择TcustomControl做为父类控件,因为它有个Canvas属性,我们可以利用Canvas在控件表面作图。不选用Tcontrol的原因是因为它有很多我们不需要的属性。

2)、ABitmap.LoadFromResourceName(hInstance, ABitmapId);是从资源文件中读取IdAbitmapId的位图,关于资源文件的使用请参考其他相关资料。注意代码中的“{$R *.res}”,它的作用是将资源文件编译到程序文件中,如果没有这个预编译条件,程序将会出现错误。

3)、StretchBlt是将位图画到画板上,使用方法请参考MSDN

4)、我们为控件增加了IsOn属性。这个布尔属性用来表示开关的状态(开/关)。

property IsOn: Boolean read FIsOn write FSetIsOn;我们可以看出这个属性是个可读可写的属性。当读这个属性时会将FisOn的值返回给调用者,而写属性时则会调用FsetIsOn方法,并将赋给属性的值做为参数传递给FsetIsOn。在FsetIsOn方法中,有如下实现代码:

  FIson := AValue;

  Invalidate;

首先将Fison设置为参数传递来的值,然后调用  Invalidate;要求重画控件,以告诉用户控件的状态已经改变,这一点是使用写字段无法做到的。

5   

    FPicOn: Graphics.TBitmap;

FPicOff: Graphics.TBitmap;

是声明两个.Tbitmap类型变量以保存控件的开关两种状态的图案。

6

    procedure Click;override;

procedure Paint;override;

分别是覆盖父类中相应的调度方法。当控件被鼠标单击时,Click方法会被调用,我们将在Click中改变控件的开关状态;Paint方法则在用户调用  Invalidate方法或控件发生重画时调用,我们一般在这个方法绘制控件的图案。

7)、TcustomControl中又很多事件处理句柄。比如OnClickOnKeyDown等,但是它把他们声明成了Protected保护级别,所以我们在Object Inspector中看不到他们,如果我们要他们可以在Object Inspector中被用户编辑的话,只要在Published中重新声明他们即可,不用写他们的读写方法,只要使用:Property  属性名;

这样的方法就可以。比如这个例子中的:Property Onclick;

思考题:

1、        做一个有特效的按钮控件,当鼠标按下时按钮是一个红色边框的空心圆,当鼠标松开时按钮是一个淡绿色边框的空心圆。

2、        对于父类控件中为protected的属性,如果想将它在子类控件中公布,应该怎么做?请思考Delphi为什么要将一些属性设为protected级别?

posted @ 2005-10-21 23:29 CowNew开源团队 阅读(2456) | 评论 (0)编辑 收藏

仅列出标题
共30页: First 上一页 22 23 24 25 26 27 28 29 30 下一页