好像找不到frame的句柄!

解决方案 »

  1.   

    你的frame不能脱离form而存在啊给form发消息
      

  2.   

    转载:
    Delphi 5引进了一个新的可视容器类,它代表了快速程序开发(RAD)的一个主要的进展。这个类,TFrame,使你能够可视化的设置一组含一个或更多组件,之后你可以在程序中重用它。这一能力是如此的有力,以至于Delphi 5的集成开发环境(IDE)被重新设计,以便广泛利用框架。
    本文首先概括讨论框架是什么,它通过了什么样好处。接着演示如果创建框架,如何修改框架实例中对象的属性。然后你将学习如何创建框架的事件处理函数,和如何在框架实例中重载或扩展这些事件处理函数。最后演示如何将框架加入到组件面板(Component Palette)和对象仓库(Object Repository)和这样做的好处。
    框架概观    框架有两个主要的好处。第一,在某些情况下,框架可大幅减少需要存储在工程中的资源量。第二,也是更重要的好处,框架允许你可视化的创建能复制和扩展的对象。对可视化窗体继承(VFI),你可以享受同样的好处。    VFI允许你很简单的创建由继承得来的窗体。VFI的限制是你必须用“全部或全无”的方式来使用窗体。更具体的说,当你用VFI时,你总是创建一个新的窗体。框架,相反的,在这一方面更类似面板(panel),这就是说一个窗体可包含两个或更多框架。每一个框架保持它与父TFrame的关系,即对父类的改变将自动被实例继承。这一点是很重要的。虽然你可以用TPanel组件达到同样的效果,但它只局限于基于代码的操作,即你必须写代码来手工定义TPanel的子孙。相反的,框架被可视化的设计,就像窗体一样。    框架也可认为与组件模板(含一个或更多组件并用Component | Create Component Template命令保存到组件面板的一个组)相似。但是,相似之处仅限于它们都是被可视化设计的(不象传统的完全基于代码的组件设计)。实际上,框架与组件模板的差异是巨大的。正如我们已学过的,框架是一定义类的实例,当定义类发生改变时框架也将发生变化。与之相比,组件模板是组件的集合。对组件模板的改变不影响以前从这一模板创建的对象。
    创建框架    以下步骤演示如何建立一个框架(工程的源代码可供下载,参看文章的结尾部分)。1)选择 File | New Application 建立一个新的工程。
    2)选择 File | New Frame 建立一个新的框架。在这一框架上,添加三个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的事件处理函数(代码可以在本文的下载文件中的Frame2工程中找到):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; 
     框架节省资