Delphi中TFrame组件的使用

        从Delphi 5.0开始,Borland 引进了一个新的可视化的容器类TFrame。
        这个类,我们称之为TFrame框架组件,使程序开发人员能够可视化的设置一组组件,之后系统中对它进行重用。
TFrame框架概观
        TFrame框架有两个主要的好处:
        第一、框架可大幅减少需要存储在工程中的资源量。
        第二、框架允许你可视化的创建能复制和扩展的对象。对可视化窗体继承(VFI),你可以享受同样的好处。
        VFI允许你很简单的创建由继承得来的窗体。VFI的限制是你必须用“全部或全无”的方式来使用窗体。更具体的说,当你用VFI时,你总是创建一个新的窗体。框架,相反的,在这一方面更类似面板(TPanel组件),这就是说一个窗体可包含两个或更多框架。每一个框架保持它与父TFrame的关系,即对父类的改变将自动被实例继承。这一点是很重要的。虽然你可以用TPanel组件达到同样的效果,但它只局限于基于代码的操作,即你必须写代码来手工定义TPanel的子孙le。相反的,框架被可视化的设计,就像窗体一样。
        TFrame框架也可认为与组件模板(含一个或更多组件并用Component | Create Component Template命令保存到组件面板的一个组)相似。但是,相似之处仅限于它们都是被可视化设计的(不象传统的完全基于代码的组件设计)。实际上,框架与组件模板的差异是巨大的。正如我们已学过的,框架是一定义类的实例,当定义类发生改变时框架也将发生变化。与之相比,组件模板是组件的集合。对组件模板的改变不影响以前从这一模板创建的对象。
创建框架
        以下步骤演示如何建立一个框架:
1>  选择 File | New Application 建立一个新的工程。
2>  选择 File | New Frame 建立一个新的TFrame框架。在这一框架上,添加三个Label和三个DBEdit,还有一个DBNavigator和一个DataSource。将Label的Caption分别设为ID,First Name和Last Name。将DBEdit和DBNavigator的DataSource属性设为DataSource1。
3>  将框架的Name属性设为NameFrame。(相比其它对象,起一个有意义的名字对框架来说非常重要)最后,选择File | Save As 保存框架。在这里,用文件名NAMEFRAM.PAS保存框架。  
这就是建立一个框架的所有步骤。
使用框架
        以下步骤演示如何使用框架:
1>  在由上面步骤建立的工程中选择Form1。
2>  加两个GroupBox到窗体上,其中一个在另一个之上。设置第一个的Caption为Customers,第二个的Caption为Employees。
3>  现在添加框架。选择组件面板的Standard页,点击Frame组件并将其拖到名为Customers的GroupBox中。这时Delphi会显示一个Select frame to insert对话框。
4>  在对话框中选择NameFrame。现在框架将在名为Customers的GroupBox中显示。重复这一步骤,这一次将框架放在名为Employees的GroupBox中。你可能要调节框架的尺寸,这跟你最初是如何放置有关。
5>  将两个Table组件放到窗体中,将其DatabaseName属性都设为IBLocal。将Table1的TableName属性设为CUSTOMER,将Table2的TableName属性设为EMPLOYEE。将两个Table的Active属性都设为True,使它们有效。
6>  下面的步骤将把事情变得有趣。选择名为Customers的GroupBox中的DataSource,将其DataSet属性设为Table1。一般的你不能直接选择组件中的对象,但是框架是个例外。你可以选择框架中的任何对象,操作它们的属性。然后,选择名名为Customers的GroupBox中DataSource,其DataSet属性设为Table2。
7>  最后,设置好所有的DBEdits。将名为Customers的GroupBox中的三个DBEdits的DataField属性分别设为CUST_NO,CONTACT_FIRST和CONTACT_LAST。对Employees中的,设置DataField属性为EMP_NO,FIRST_NAME和LAST_NAME。
8>  保存工程并运行。
框架和继承
        到此为止,使用框架似乎没有什么好处。但是,当你要在一些地方使用同一个框架,然后又要改变所有这些实例时,框架的威力就表现得很明显了。例如,假设你要使所有的NameFrame框架变为只读的,你只需要将初始的框架修改,所有的修改就会被框架实例立刻继承。
  
        你依照如下步骤就可以验证这一点:
1>  在上面建立的工程中,按[Shift][F12]并在窗体列表中选择NameFrame。
2>  将DataSource的AutoEdit属性设为False 。
3>  然后,选择DBNavigator,展开它的VisibleButtons属性,并设置nbInsert,nbDelete,nbEdit,nbPost和nbCancel标志为False。
4>  现在看一下你的主窗体,注意两个NameFrame的后代都继承了你对框架做的修改。 
重载包含组件的属性
        框架的优点之一(与VFI一样)是你可以改变框架中对象的属性和事件处理函数。这些修改重载了继承的值。说得具体些,随后对初始框架的重载属性的修改将不改变后代的重载属性的值。以下步骤可以验证这一行为:
1>  在名为Customers的GroupBox中,选择标题为"ID"的label,在Object Inspector将其Caption属性改为“Customer No:”。现在选择名为Customers的GroupBox中的标题为"ID"的Label,其Caption属性改为“Employee ID:”。
2>  按[Shift][F12]并选择NameFrame。将标题为"ID"的Label的Caption属性改为Identifier。
3>  回到主窗体,注意Label的Caption属性没有变为Identifier。它们仍保持它们的重载值。
4>  这一效果是由保存在DFM文件中的信息实现的。
        注意所有其属性改变过的在框架中的对象,都出现在DFM文件中的内置部分。但是,这一部分仅列出那些改变的值,所有其它属性值要么按初始框架的值设置(它们已经存储在框架的DFM文件中),要么按每一组件的默认类定义值。
包含组件的事件处理函数
        框架中的对象也可以有事件处理函数。虽然事件只是一个方法指针型属性,但它们的默认行为被重载后的处理与其它属性不同。
        让我们先考虑一下框架对象的事件处理函数是如何定义的。假设一个框架有两个按钮,一个标为Help,另一个标为Done(显然按钮的标题可以在框架后代中重载)。这两个按钮都有onClick的事件处理函数:
procedure TTwoButtonFrame.Button1Click(Sender: TObject);
begin
    if  (TComponent(Sender).Tag = 0)   or   (Application.HelpFile = '''')   then
            MessageBox(Application.Handle,''Help not available'', ''Help'',MB_OK)
    else
            Application.HelpContext(TComponent(Sender).Tag);
end;
---------------------------------------------------------------------------------------------------------
procedure TTwoButtonFrame.Button2Click(Sender: TObject);
var
    AParent: TComponent;
begin
    AParent := TComponent(Sender).GetParentComponent;
    while not (AParent is TCustomForm) do
               AParent := AParent.GetParentComponent;
    TCustomForm(AParent).Close;
end;
 
        就象窗体中的对象的事件处理函数是窗体类的公开方法,框架中的对象的事件处理函数也是框架类的公开方法。(代码段实际上并没有说框架的这些方法是公开的,而是它们在框架声明的默认可见部分被声明,其可见性默认是公开的。)
  
        如果你查看与Done按钮相联系的Button2Click事件处理函数的代码,你会发现与框架相联系的事件处理函数引进了一个很有趣的技术。具体的说,由于Self是框架而不是包含框架的窗体,因此不能在函数中调用Close方法来关闭窗体。当你在代码中使用没有限定的方法时,编译器将认为你要使用Self的方法。因为TFrame对象没有Close方法,编译器将产生一个错误。
        因为在本例中框架被设计为内置在一个窗体中,事件处理函数用GetParentComponent方法向上找TCustomForm实例(它要么是TForm的后代,要么是基于TCustomForm的定制窗体),如果找到了,就调用窗体的Close方法。
  
重载包含对象的事件处理函数
        如果你对VFI中的事件重载比较熟悉,你会回忆起Delphi在后代窗体的重载的事件处理函数中内置继承的事件处理函数。你可以改变生成的代码,在调用继承函数之前或之后添加附加的代码,或是有条件的调用继承的事件处理函数,或是干脆省略对继承的事件处理函数的调用。
        框架的后代当使用父框架中对象的事件处理函数时,不使用继承的函数,而直接调用祖先框架的方法。例如,你把TwoButtonFrame放到一个窗体中然后双击,Delphi将产生如下的代码:
  
procedure TForm1.TwoButtonFrame1Button2Click( Sender: Object);
begin
      TwoButtonFrame1.Button2Click(Sender);
end;
  
        在产生的代码中,TwoButtonFrame1是TTwoButtonFrame(最初的框架类)的后代。Button2Click,如你在先前的代码段中所看到的,是Done按钮的事件处理函数。结果是,这一代码调用最初的事件处理函数,将框架实例中传给按钮的Sender传给它。
        这意味着事件处理函数引进了另一个有趣的特性。具体的说,在这种情况下,Sender一般不是Self对象的成员。实际上,Sender一般是窗体对象的成员,而Self是框架对象。
        在这里,原来的行为被“注释掉了”,这样新行为就完全替代了原来对Done按钮定义的行为。
procedure TForm1.TwoButtonFrame1Button2Click( Sender: TObject);
begin
     with TForm2.Create(Self) do begin
             ShowModal;
             Release;
     end;
     // 下面是原来自动生成的代码
     //  TwoButtonFrame1.Button2Click(Sender);
end;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DELPHI动态创建删除FRAME unit Unit2; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,FM; type TForm2 = class(TForm) Panel1: TPanel; Button2: TButton; ScrollBox1: TScrollBox; procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end; type TFM = Array Of TFrame1; var Form2: TForm2; aFM: TFM; procedure DeleteArrItem(var arr: TFM ; Index: Integer);stdcall; implementation {$R *.dfm} procedure DeleteArrItem(var arr: TFM ; Index: Integer); var Count: Cardinal; i:integer; begin Count := Length(arr); if (Count = 0) or (Index = Count) then Exit; Move(arr[Index+1], arr[Index], (Count-Index)* SizeOf(arr[0])); SetLength(arr, Count - 1); for I := 0 to Length(arr) - 1 do begin arr[i].Label1.Caption:=inttostr(i); end; end; procedure TForm2.Button2Click(Sender: TObject); var fram:TFrame1; begin SetLength(aFM,length(aFM)+1); aFM[length(aFM)-1] :=TFrame1.Create(nil) ; fram:=aFM[length(aFM)-1]; fram.Label1.Caption:=inttostr(length(aFM)-1); fram.Parent:=ScrollBox1; end; end. unit fm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TFrame1 = class(TFrame) GroupBox1: TGroupBox; Label1: TLabel; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; implementation uses unit2; {$R *.dfm} procedure TFrame1.Button1Click(Sender: TObject); begin DeleteArrItem(aFM,strtoint(label1.Caption)); ( Sender as Tbutton ).Parent.Parent.Destroy; end; end.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值