一个窗体分成左上、右上和下部等三个部分,左上窗体是一个treeview,右上是一个form,下部是一个list。双击treeview中的不同项将使右上的窗口显示不同的form。
由于右上窗口的form太多,其中的控件也很多,把所有控件做在一个窗体上,通过程序显示和隐藏特定控件的路子难以实现。
不甚感激。

解决方案 »

  1.   

    左上放treeview,右上放一个panel,
    每次treeview被click时,动态创建右上的form,置form的parent为panel再显示。
    记住要关闭上一个form.
      

  2.   

    一个窗体....右上窗口的form太多...什么啊,不懂
      

  3.   

    gub的做法有可能是行得通的。一个窗体....右上窗口的form太多...什么啊,不懂treeview的每个item都对应一个form,点不同item出现不同form,所有这些form都出现在右上那个窗口中。当然可以只用一个form,而把所有控件都放在它上面,在单击不同item时出现不同控件,但我的form有几十个,控件有数百个,放在一个form上是不是太恐怖了。
      

  4.   

    用如下语句出现异常。
      Form2.CreateParented(Panel1.Handle);
      

  5.   

    但我的form有几十个,你可以考虑用PageControl.
      

  6.   

    个人理解:
      这里用PageControl不是很好。(我最喜欢用PageControl了,因为我喜欢做wizard)。
      窗体创建的时候,一大堆的控件被创建,占用内存等等to 楼主:
      from1:=Tform1.create(self);
      form1.parent:=Panle1;
      form1.align:=alClient;
      form1.show;
      

  7.   

    我用过这个方法,肯定是可行的。
    另外,要判断panel1中是不是有窗体,有就要free.
      

  8.   

    方法一、使用框架
    方法二、使用组件NoteBook(Win3.1)
      

  9.   

    给你看个例子:
    建立2个form,分别为form1,form2,
    在form1上增加:button1,panel1,panel的docksite属性设置为true;form2的dragking属性设置为dkDock;在form1的button2的click事件中:
        Form2.Show;
        Form2.ManualDock(Panel1, nil);
        form2.FormStyle:=fsStayOnTop;
    看看做了这个列子可不可以帮你。
      

  10.   

    给你看个例子:
    建立2个form,分别为form1,form2,
    在form1上增加:button1,panel1,panel的docksite属性设置为true;form2的dragking属性设置为dkDock;在form1的button1的click事件中:
        Form2.Show;
        Form2.ManualDock(Panel1, nil);
        form2.FormStyle:=fsStayOnTop;
    看看做了这个列子可不可以帮你。
      

  11.   

    这样生成的新窗口是有一个独立的窗口,是有最大化、最小化等按钮的,我希望的是类似于资源管理器的界面,但它的右边不是一个list,而是一组form。
      我下了一个局域网浏览器,感觉太复杂了,而且和我的要求也不太一样。
      麻烦各位出出主意,多谢!
      

  12.   

    我的主意可能不好,大家不要见笑,你可以把一组Panel,它是容器组件,把一组Panel做成同样大小,重叠放在一起,你只要点TreeView节点,就把其中一个BringToFront
      

  13.   

    lincanwen(密码错误) 的方法是浮动窗口,我是想要资源管理器那样。
    geyobing(银翼天使) 的方法可能比较耗内存:“窗体创建的时候,一大堆的控件被创建,占用内存”。附带问一句,我的三个panel的align应该设,splitter是不是要2个        |
         A  |  B
            |
    ________|_________        C
      

  14.   

    推荐用gub的方法,右下放一panel,
    根据需要动态生成form,并把生成的form的parent设为panel,
    其实最好应该使用Tframe,比较方便。
      

  15.   

    to fengxvhui(学得快忘得快)
    你可以设置 动态form的borderstyle 为bsNone啊。
    绝对没问题的。我是从DevExpress的一些例子的源码里学到的。
      

  16.   

    多谢!问题解决了。
    不过还有两个相关问题。
    我改变了主意,希望在一个panel中出现多个窗口(以前是只有一个,这个问题已经解决)
    1. 怎样使一个panel中的多个窗口水平平铺、垂直平铺?
    2. 怎样释放一个form,是用free还是用destroy?用父类变量,如TForm去释放子类变量,如TFormN会有问题吗,要不要声明为virtual。
      

  17.   

    问题还没有解决。
    新建的窗口的标题栏是灰的,上面的edit控件不能接受焦点,上面的button倒是可以接受click事件。
    对我来说,标题栏是灰的倒无所谓,但edit肯定要能够编辑啊。
      

  18.   

    我试出来了。
    如果borderstyle为默认的bsSizable,要先按右键,此时edit中所有文本将被选中,再点左键,就可以编辑了;
    如果borderstyle为bsNone,则和通常一样,直接点左键就可以了。我希望能在同一个panel中显示多个窗口,能对这多个窗口设置平铺方式,并且能够直接点左键就可以编辑edit。另外,动态生成TFormN再赋给TForm将不能访问TFormN的新增成员(父类对象不能访问子类对象的成员)。怎样获得TForm所指对象的类型(是指向哪个TFormi),再进行类型转换,以访问子类对象的成员。
      

  19.   

    哇,好多问题啊
    呵呵,有些我就没实践过了,只好先出点子。
    1. 引申啊
      建一个MDIForm,它的parent为panel1,borderstyle为bsNone,align为alClient;
      其他的form都是MDIForm的子form啦;
      这样,平铺什么的不是很方便吗?
    2. free会去调Detroy的;所以我们free就可以了
      

  20.   

    最后一问
    “另外,动态生成TFormN再赋给TForm将不能访问TFormN的新增成员(父类对象不能访问子类对象的成员)。怎样获得TForm所指对象的类型(是指向哪个TFormi),再进行类型转换,以访问子类对象的成员。”
    不太明白。
    烦请说的详细点。
      

  21.   

    treeview中的项如果是固定的,我就有办法,MDIForm建议不要用。
    如果不急,可以给你做个例子。
      

  22.   

    类成员
      MyForm: array[1..n] of TForm;treeviewclick事件
      MyForm[FormCount] = TFormI.create(self); //单击第i个item,生成对应FormI,并保存之。因为不知道用户会单击哪个item,只能保存到类型为基类TForm的数组中
      FormCount := FormCount + 1;close事件
      for i:= 1 to FormCount
         if (MyForm[i].bDataIsChanged) //如果第i个form中的数据发生变化,则保存数据,此时需要访问子类对象定义的成员bDataIsChanged,但MyForm[i]是基类对象,不能访问子类对象的成员bDataIsChanged,因此需要类型转换,从而又需要知道MyForm[i]所保存form的类型,也就是说,判断出它所保存的,到底是TFormN中的哪一个。
      

  23.   

    项是否固定有影响吗?
    我用树来表示类别,类别是固定的,但每一类可以有多个对象,而对象的数目是不定的,可以为0到无穷。
    如果对象也是固定的,就可以为每个对象都声明一个form变量,单击哪个对象(item)就使用相应的form变量,这样就把通过基类对象访问子类对象成员的问题给绕过去了。
      

  24.   

    实在逼急了,当然可以对每一类声明一个指针变量,通过动态内存分配来分配等于该类对象数目的空间来保存这些form,不过我觉得“通过基类变量访问子类对象成员”和“判别基类变量所保存对象的类型”这两个问题应该是可以解决的,对吗。
      

  25.   

    刚才已经有了个解决方法。
    可是一连回复了三次。又没有马甲,不能回复了。
    其实有个简单的办法。D7下已经测试成功。如下:主form设为MDIForm,其他form设为MDIChild
    不用建panel1了。
    直接放C区的panel,alBottom
    再放splitter1,alBottom
    再放A区的TreeView,alLeft
    再放splitter2,alLeft.
    然后点击TreeView的节点时,创建各MDIChild Form就是了。
    当然Casacade,Tile都有了。
      

  26.   

    不好意思,最近我比较迟钝。总算看懂最后一问的意思了。
    硬的解决方法是有的,不过我觉得比较麻烦。为什么不变一下思路?
    子form的保存 由 子form自己在close的时候进行,
    这样主form只close各子form就行了。
    这样也符合面向对象的思想啊。这样做有什么不便,请再说明。
      

  27.   

    硬的办法如下:使用类型参考
    type
      TFormClass = class of TForm;建两个数组:
    类成员
      MyForm: array[1..n] of TForm;
      MyFormClass: array[1..n] of TFormClasstreeviewclick事件
      MyForm[FormCount] := TFormI.create(self); //单击第i个item,生成对应FormI,并保存之。因为不知道用户会单击哪个item,只能保存到类型为基类TForm的数组中
      MyFomClass[FormCount] :=TFormI
      FormCount := FormCount + 1;close事件
      for i:= 1 to FormCount
         if ((MyForm[i] as TMyFormClass[i]).bDataIsChanged) //如果第i个form中的数据发生变化,则保存数据,此时需要访问子类对象定义的成员bDataIsChanged,但MyForm[i]是基类对象,不能访问子类对象的成员bDataIsChanged,因此需要类型转换,从而又需要知道MyForm[i]所保存form的类型,也就是说,判断出它所保存的,到底是TFormN中的哪一个。
      

  28.   

    不好意思,又是一堆问题。先介绍一下程序的总体界面。程序经过用户验证后,将出现一个欢迎界面(不是splash窗口,而是用户刚刚进入系统的主界面)。用户单击菜单的哪一项,与这一项对应的窗口就出现,而原来的当前窗口则被关闭。我们正在讨论的那个包括三个部分的窗口也是与菜单中某一项对应的窗口,它可能随用户单击它所对应的菜单项而出现,也可能随用户单击其它菜单项而被关闭。这样,对于一个窗口而言,它可能因为程序的关闭而关闭,也可能因为用户单击其它菜单项而被关闭。现在来说存在的问题。子form的保存由子form自己在close时进行是一个好主意,我没注意到这种思路。但是有两个问题:
    1 在主form被关闭时如何通知子form执行close操作
      是否是主动调用子form的close。如果是的话,用下面的语句是否可行,在子form的close中写数据库是否来得及(因为窗口马上就要关闭了)。
        for i:= 1 to FormCount
         MyForm[i].close; //MyForm是TForm型变量,它保存了TFormN型对象,但它不知道所保存的是TFormN中的哪一种
    2 子form如何主动通知主form它已被用户关闭
      因为用户完全可以主动关闭某一个子form我用delphi 6实现了在delphi 7下使用MDIForm和MDIChild的方法,是可行的,但MDIChild最大化后的效果不太好:
    3 整个程序的标题栏
      把MDIChild最大化后,整个程序的标题栏出现了 [Form2] 的字样,这对通常的MDI程序来说是合理的,但我不希望出现这些字样,如何重载它的最大化事件,使得不出现这些字样。
    4 MDIChild的标题栏消失了
      把MDIChild最大化后,MDIChild的标题栏消失了,而通常的MDI程序是有两个标题栏的,一个是整个程序的,一个是MDIChild自己的,delphi的MDI demo可以实现这样的效果,但我没看出来是哪个地方使它具有这样的效果。当然,这个问题不是非常重要,不能解决也问题不大。5 对于上面的这种方法,能否为B区设一个单独的工具栏,我想把诸如水平平铺、垂直平铺之类的按钮放在这个工具栏上。当然,这个问题也不是非常重要。6 如何把客户区中的当前form用另一个form给替换掉
      因为不同的菜单项对应不同的form,单击菜单的某一项,当前form就消失,该菜单项相应的form就create和show。我想了一个最笨的方法,就是把这个菜单放到每个form上去,用户以为是替换的客户区,事实上却是替换了整个form。但这样做代码的冗余就大为增加了,而且也很麻烦。不知有什么好的方法。
      

  29.   

    1. 主form关闭时,只要用
     for i:= 1 to FormCount
       if MyForm[i]<> nil then  //MyForm[i]没有被释放
          MyForm[i].close;
       在OnClose里面执行操作是没问题的。因为,它实际发生在form真正关闭前。
       这里可能会有问题,因为主form不知该调用哪一个Close.因为它不知道MyForm[i]是什么
    类的。也许它会调用TForm.close。所以,还是使用类型参考吧。(见上贴)2. 子form关闭(OnClose的最后)时,加一句:
      action:=caFree;
      formI:=nil;//表示Formi此时已被释放3. 实在不行,在每个子form的OnMaximize里面写上:
      caption:='';
      再在OnRestore/OnMinimize里把caption改回来。4. 试试在OnMaximize写个控制,不让MDIChild最大化。
       
    6. 如果同时不让用户看到多个form,为什么不showmodal呢? 
    当然不showmodal也是可以实现的。
    能不能定义一个Tform变量,记住当前那个form呢?这几天比较忙,可能没时间测试了。
    仓促之间的一些想法,如果误导了您,请见谅。
      

  30.   

    1. 我试了一下,它竟然会调用子类的close,我真不明白,它能找到子类的close,为什么不能访问子类的数据成员。
    2. MDI的mainform应该能够知道它的某个childform被用户关闭了吧。如果使用formI:=nil;就要求查找主form中的数组,因为子form并不知道它被保存在数组的第几个元素中。除非我们为每个item都在数组中保留固定的位置。
    3、4 实在没办法,就不允许用户最大化了
    5
    6 如果showmodal,用户虽然不能切换到主界面,但他是可以看见主界面的,除非我们能够保证其它所有界面都比主界面大,而且用户不能移动当前的界面。至于用TForm变量,我不知道该怎么做。这几天的讨论使我受益匪浅,作为唯一的表示谢意的方法,我一定会另外开帖,恭敬给分,以此来表达我心中的谢意!
      

  31.   

    5 我的工具栏不是放在A和B的上部(左图),而是放在B的上部(右图),因为这个工具栏的操作对象是B中的窗口。    toolbar                                                  |  toolbar
           |                                                 A   |     B
        A  |  B                                             _____|_______
      

  32.   

    1. 我表达有误。
      form的close我们并没有重载,应该可以找到。
      我的意思是主form能否找到某个子form的OnClose事件代码。因为他不知道该子form的类。2. 这种写法并不麻烦,多加一行if语句而已;效率影响也不大;
      而主form中的数组中存放的只是FormI的引用(指针而已),也不怎么耗内存。6. MDIForm有个属性:ActiveMDIChild
      每次打开下一个form的时候,
      if self.ActiveMDIChild <> nil then
        self.ActiveMDIChild.close;  
      就可以关闭了。不用定义TFrom变量来记忆当前子form了,除非另有需求。5. 这种layout的话,就不那么方便了。因为你还有两个splitter呢。
      能控制吗?试过才知道。另外,我也是菜鸟一只。
    我的回答,大部分也是即时摸索出来的,自己也有收获。
    大家交个朋友,共同学习。
      

  33.   

    1. 你没有说错。我在主form中定义了两个Tform型变量,用它们来保存新建的两个子form:form2和form3,再在form2和form3的close事件中写了一句MessageBox和cafree,最后在主form上放了一个button,在它的click事件中通过这两个Tform型变量调用close方法(没有任何类型转换),没想到那两个MessageBox竟被执行了。敢情在delphi中close事件已被定义成virtual了。
    2. 我明白了你的意思。如果在C语言中,就是 p=&form; form = NULL; if (*p==NULL)
       只是我不明白TFormN不是类吗,能对以它为类型的对象赋nil吗,create返回的是对象本身还是指向对象的指针,在C语言中返回的是对象的指针。
    3、4、5没感觉,反正不是非解决不可,先跳过去再说。
    6 我不知道我的理解与你说的是否一样。就是由菜单单击出现的所有form都是子form,而所讨论的出现在B区的几个form又是其中某一个子form的子form。如果子form可以有多级,也就是说,一个form可以作为上一级的子form,同时又作为下一级的父form,这种方法是行得通的。
      

  34.   

    1. 爽。学到了。2. formI是指针啊。可以赋值为nil。
    同样,当你定义
      var 
        frm:TForm;
      ...
      frm:=formI;
    的时候,frm只是FormI的引用而已。6. form应该是不能多级吧。           ...... (I)
    看来这个问题上我没理解清楚你的意思。
    你的意思应该是:
    (A) mainform每个菜单项打开一个对应的form;
    (B) 打开对应form时,关闭已有的form;
    (C) 某个菜单项对应的form是MDIForm;
    (D) 菜单项对应的form不能showmodal;
    (E) 由(I),mainform不能是MDIForm
    Question: 怎么实现(B)?   我现在的理解有没有错?如果没错,我的想法如下:
    (i)  在mainform定义一个Public的CurrentForm:TForm;
    (ii) 创建mainform的子form用这样的代码:
      if CurrentForm <> nil then CurrentForm.Close;
      CurrentForm:=Tform3.Create(self);
      CurrentForm.Show;
    (iii)各子form use mainform
    (iv) 子form的OnClose事件中
      action:=caFree;
      mainform.CurrentForm:=nil;
      

  35.   

    不妨设mainform的子form为formi(i=1,2,…,N),第i个子form的子form为formij(j=1,2,…,M)
    我应该如何设置mainform、formi和formij的formstyle。
    如果mainform、formi是normal,而formij是child,能保证formi始终在mainform的客户区中吗?
    如果mainform是normal,而formi和formij是child,那formij怎么知道它的父窗口应该是formi,而不是mainform。
                            mainform
                         /      |     \
                     form1    formi   formN
                             /  |   \
                       formi1 formij formiM
      

  36.   

    1. 这样的话,formi不能用MDIForm了。(试过不行)
    2. mainform也许可以用MDIForm。(我没试过)3. 整理一下本帖中的绝大部分问题(也许还有没理解透的)的思路如下:
    A). mainfrom对formL,formI,formN的调用,采用我上贴的解决方法。
    即:用CurrentForm来记住当前窗体
    B). formI对formI0, formI1, ... ,formIn的调用,还是采用前述用Panel作parent的想法。
    C). 平铺的控制只好自己写罗
    简单的两个水平、垂直我已经测试过了
    procedure TForm2.Button1Click(Sender: TObject);
    var
      i:integer;
    begin
      for i:=0 to panel2.ControlCount-1 do
        with Panel2.Controls[i] do
          Align:=alNone;
      for i:=0 to panel2.ControlCount-1 do
        with Panel2.Controls[i] do
        begin
          Width:=Panel2.ClientWidth div panel2.ControlCount;
                //Width:=Panel2.ClientWidth;
          Height:=Panel2.ClientHeight;
                //Height:=Panel2.ClientHeight div panel2.ControlCount;
          Align:=alLeft;
                //alTop
        end;
    end;
      

  37.   

    使用动态TFrame解决比什么方法都好(Tframe就是用来分割窗体的),为什么不试一试,而去用些笨办法
      

  38.   

    TO maozefa:
      愿闻其详,就本帖说说。我愿另外开贴给分。
      

  39.   

    to maozefa:
       敬请赐教,分不是问题。
      

  40.   

    frame我用的不多,主要用它进行相似界面的重用。
    主要的不便是:没有OnCreate/OnClose/OnShow(有OnResize)事件。1. mainform对子form的控制可以用frame,也许方便一点,差别不大。如:
      private
        { Private declarations }
        frame:TFrame;  ...
      
      if frame<>nil then frame.Free;
      frame:=TframeI.Create(self);  //I=0..n
      frame.Parent:=Self;
      frame.Align:=alClient;
      frame.Show;
      //类似form的控制。不用写form.OnClose。2. 但是frame相关的数据操纵,个人觉得不如form。
      

  41.   

    在frameI(formI)和frameIJ(类似formI和formIJ)的控制中,
    frameIJ可能由用户关闭,也可能由frameI(formI)关闭。
    这时frameIJ数据的保存,该放在哪儿呢?
      

  42.   

    我把问题整理了一下。            非客户区(菜单和工具栏),记作mainframe
                         /      |     \
                     form1    formi   formN
                             /  |   \
                       formi1 formij formiM如上图所示,程序中的form分为两级。
    第一级的N个form共用同一个非客户区(菜单和工具栏),用户登录后将出现form1,日常运行时则通过单击菜单进行N个form之间的切换,任何时刻有且仅有一个第一级form。
    在第一级的form中,有的只有它自己一个窗口,有的则还有自己的子窗口。           |
            A  |  B
               |
       ________|_________           C如上图所示,A、B、C共同构成有子窗口的第一级formi的客户区,它们之间的相对大小可由用户动态调整。
    formi的子窗口formij只能局限于B区,其数目是由用户实际的操作(双击A区中treeview的item,此时增加一个formij;和主动单击formij的关闭按钮,此时相应的formij被关闭)所决定的,没有事先的限制(可以为0到无穷)。
    每个formij在启动时读数据库,在关闭时写数据库,在formi因为用户单击了其它菜单项而被替换和因为主程序被关闭而被关闭时也要写数据库。希望能实现批量写数据库,例如不是在formij关闭时写数据库,而是在formi被关闭时写数据库,这样速度可能会快一些。
    对于B区中的formij,要求在B的范围内支持层叠和平铺操作(不能超出B的边界),并且希望在B区能有一个独立的工具栏,即如右下图所示。                                                     mainframe's toolbar
     mainframe's toolbar                                           | B's toolbar
             |                                                 A   |     B
          A  |  B                                             _____|_______
    _________|________请各位朋友多多指教,本人不甚感激。
      

  43.   

    To gub(gub)
    主要的不便是:没有OnCreate/OnClose/OnShow(有OnResize)事件。贴一个小元件做补丁:unit FrameMove;interfaceuses
      SysUtils, Classes, Controls, Forms, Dialogs;type
      TFrameQueryEvent = procedure(Sender: TObject; var Enable: Boolean) of Object;  TFrameMove = class(TComponent)
      private
        { Private declarations }
        FParentLeft: Integer;
        FParentTop: Integer;
        FOnInsertQuery: TFrameQueryEvent;
        FOnRemoveQuery: TFrameQueryEvent;
        FOnInsert: TNotifyEvent;
        FOnRemove: TNotifyEvent;
        function GetFrame: TFrame;
        function GetIsInsert: Boolean;
        procedure SetParentLeft(const Value: Integer);
        procedure SetParentTop(const Value: Integer);
      protected
        { Protected declarations }
        function CanInsert: Boolean; virtual;
        function CanRemove: Boolean; virtual;
      public
        { Public declarations }
        constructor Create(AOwner: TComponent); override;
        function InsertFrame(FrameParent: TWinControl): Boolean;
        function RemoveFrame: Boolean;
        property Frame: TFrame read GetFrame;
        property IsInsert: Boolean read GetIsInsert;
      published
        { Published declarations }
        property ParentLeft: Integer read FParentLeft write SetParentLeft default 0;
        property ParentTop: Integer read FParentTop write SetParentTop default 0;
        property OnInsertQuery: TFrameQueryEvent read FOnInsertQuery write FOnInsertQuery;
        property OnRemoveQuery: TFrameQueryEvent read FOnRemoveQuery write FOnRemoveQuery;
        property OnInsert: TNotifyEvent read FOnInsert write FOnInsert;
        property OnRemove: TNotifyEvent read FOnRemove write FOnRemove;
      end;procedure Register;implementationprocedure Register;
    begin
      RegisterComponents('User', [TFrameMove]);
    end;{ TFrameMove }function TFrameMove.CanInsert: Boolean;
    begin
      Result := not IsInsert;
      if Result and Assigned(OnInsertQuery) then
        OnInsertQuery(Self, Result);
    end;function TFrameMove.CanRemove: Boolean;
    begin
      Result := True;
      if Assigned(OnRemoveQuery) then
        OnRemoveQuery(Self, Result);
    end;constructor TFrameMove.Create(AOwner: TComponent);
    begin
      if AOwner is TFrame then inherited Create(AOwner)
      else ShowMessage('TFrameMove必须放在TFrame元件上.');
    end;function TFrameMove.GetFrame: TFrame;
    begin
      Result := TFrame(Owner);
    end;function TFrameMove.GetIsInsert: Boolean;
    begin
      Result := Frame.HasParent;
    end;function TFrameMove.InsertFrame(FrameParent: TWinControl): Boolean;
    begin
      Result := CanInsert and Assigned(FrameParent);
      if Result then
      begin
        Frame.Parent := FrameParent;
        if ParentLeft <> 0 then Frame.Left := ParentLeft;
        if ParentTop <> 0 then Frame.Top := ParentTop;
        if Assigned(OnInsert) then OnInsert(Self);
      end;
    end;function TFrameMove.RemoveFrame: Boolean;
    begin
      if IsInsert then
      begin
        Result := CanRemove;
        if Result then
        begin
          Frame.Parent := nil;
          if Assigned(OnRemove) then OnRemove(Self);
        end;
      end else Result := True;
    end;procedure TFrameMove.SetParentLeft(const Value: Integer);
    begin
      if FParentLeft <> Value then
      begin
        FParentLeft := Value;
        if IsInsert then Frame.Left := Value;
      end;
    end;procedure TFrameMove.SetParentTop(const Value: Integer);
    begin
      if FParentTop <> Value then
      begin
        FParentTop := Value;
        if IsInsert then Frame.Top := Value;
      end;
    end;end.
      

  44.   

    To:  maozefa(之源)兄:
      这个思路很好,佩服的很。学习中。
      OnInsert/OnRemove事件应该是弥补OnCreate/OnClose事件的吧。
    可是测试时,没有触发这两个事件啊。是不是还有什么bug。