一、基础知识介绍

1、VCL组件的基础知识

在TWinControl类中有一个DockSite属性(boolean),它的作用是是否允许别的控件停靠在它的上面;在TControl类中有一个DragKind属性,如果要这个控件能停靠在别的控件上,就把DragKind属性设成dkDock。就这么简单,只要设置一下属性,一个支持停靠的程序就完成了。当然,上面说的只是最最基本的步骤,有了以上两步,我们就可以继续编写代码实现更复杂的功能。

2、停靠操作常用的属性

1>.TControl. TBDockHeight      // 停靠控件在停靠时的的高度;

  2>.TControl. LRDockWidth       // 停靠控件在停靠时的的宽度;

  3>.TControl. UnDockHeight      // 停靠控件在浮动时的的高度;

  4>.TControl. UnDockWidth       // 停靠控件在浮动时的的宽度;

  5>.TControl. HostDockSite      // 被停靠控件的实例;

  6>.TControl. FloatingDockSiteClass // 为停靠控件在浮动时创建容器(自动装载);

  7>.TControl. Floating       // 是否浮动

  9>.TControl. DockOrientation    // 停靠控件的方位

  10>.TWinControl .DockClientCount  // 被停靠控件里面有几个已经停靠的控件

  11>.TWinControl . DockClients   // 被停靠控件里面有已经停靠的控件的列表

  12>.TWinControl . DockManager    // 一个控制停靠的类,其实是一个ActiveX控件,和它对应的类是TDockTree.

  13>. TWinControl .UseDockManager   // 是否使用DockManager。

3、停靠操作常用的消息:

  1>.CM_DOCKCLIENT          // 在DockDrop方式前发生此消息(被停靠对象的事件?)

  2>.CM_DOCKNOTIFICATION,

  3>.CM_UNDOCKCLIENT

  如果读者想对停靠技术有更深入的了解,可以看Delphi自带的例子,路径是Delphi5DemoDocking.

4、停靠时Delphi会产生的方法和事件

1>、OnDockOver:在停靠控件掠过被停靠控件时触发的。主要作用是控制停靠控件的预览位置,出现虚线包含的范围(Source.DockRect)(是被停靠控件的事件)。

OnDockOver(Sender: TObject; Source: TDragDockObject;X, Y: Integer; State: TDragState; var Accept: Boolean);

参数说明:

  Source:包含了停靠—拖动操作的信息,其中有一个重要的属性是Control,就是停靠控件,另一个重要的属性是DockRect,就是停靠的位置;

  X,Y:是鼠标的位置,

  State:的状态有dsDragEnter, dsDragLeave, dsDragMove,分别表示拖动进入,拖动离开,拖动移动;

  Accept:是是否同意停靠的意思。

2>、OnDockDrop,在停靠控件进入被停靠控件时发生的,作用是控制停靠控件的最终位置。它才是决定停靠控件在哪里出现的罪魁祸首(是被停靠控件的事件)。

OnDockDrop(Sender: TObject; Source: TDragDockObject;X, Y: Integer);

参数说明:

  和OnDockOver差不多,只是少了State: TDragState和var Accept: Boolean。

3>、OnGetSiteInfo当停靠控件移动时触发的,所以经常触发(是被停靠控件的事件)。

OnGetSiteInfo(Sender: TObject; DockClient: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);

参数说明:

  DockClient:就是停靠控件。

CanDock:和OnDockOver中的Accept差不多,都是询问是否允许停靠。当为False时,DragOver事件不会执行。

(在这里可以不写,CanDock默认就是True,也可以写上CanDock := DockClient is TDockableForm;)

4>、OnStartDock:停靠开始时触发(是停靠控件的事件)。

OnStartDock(Sender: TObject; var DragObject: TDragDockObject);// 经常会被触发

5>、OnEndDock:停靠结尾时触发(是停靠控件的事件)。

OnEndDock(Sender, Target: TObject; X, Y: Integer);// 经常会被触发

6>、OnUnDock:不停靠(也就是被拖出来变成浮动时触发)(是停靠控件的事件?个人觉得是被停靠对象的事件)。 

OnUnDock(Sender: TObject; Client: TControl; NewTarget: TWinControl; var Allow: Boolean);//停靠窗体变成浮动时触发

二、在一个窗体中停靠另一个窗体的实例

一般的支持停靠的程序都可以在主窗口的上下左右停靠,也就是说在主窗口的边上放上能被停靠的控件比较好(只要是从TWinControl继承的都行),一般我们都选择TPanel,为了便于读者理解,我们可以假定主窗口的左边可以停靠。

1、基本步骤: 

1>、在主窗口上放一个Panel,称为被停靠控件:

设置属性Name:LeftDockPanel;Align:alLeft; width:0,DockSite:True;

2>、然后再放一个TSplitter,可以调节LeftDockPanel的大小:

设置属性Name:LeftSplitter;Align:alLeft。

3>、再建一个窗体TForm,用于停靠,称为停靠控件。

设置属性: Name:DockableForm;DragKind:dkDock;DragMode:dmAutomatic(自动停靠)。

现在我们可以运行这个程序了(鼠标要进入Panel区域才能停靠)。停靠的窗体停靠进去后就不见了!这是可以使用停靠时Delphi会产生一些事件,控制它的显示情况。

2、控制它的显示,需要添加以下事件:

1>、OnDockOver:控制停靠窗体的预览位置

procedure TMainForm.LeftDockPanelDockOver(Sender: TObject;

Source: TDragDockObject; X, Y: Integer; State: TDragState;
 var Accept: Boolean);
var
 ARect: TRect;
begin
 Accept := Source.Control is TDockableForm;
 if Accept then
 begin
//修改预览停靠位置
ARect.TopLeft := LeftDockPanel.ClientToScreen(Point(0, 0));
  ARect.BottomRight := LeftDockPanel.ClientToScreen(
   Point(Self.ClientWidth div 3, LeftDockPanel.Height));
  Source.DockRect := ARect;
 end;
end;

现在再运行程序,当你把DockableForm拖动到主窗口左边时,已经出现了预览停靠位置,也就是虚线包含的范围。

2>、OnDockDrop:控制停靠窗体的最终位置

procedure TMainForm.LeftDockPanelDockDrop(Sender: TObject; Source: TDragDockObject; X, Y: Integer);
Begin
 LeftDockPanel.Width := ClientWidth div 3;
 LeftSplitter.Left := LeftDockPanel.Width + LeftSplitter.Width;
End;

现在再运行程序,出现了一个和Delphi的IDE完全一样的停靠窗体,上面是两条横线,用来把它拖出来,右上角有一个小X是用来关闭的。

3>、onClose:停靠窗体的关闭事件中控制侧边栏的关闭

点击右上角那个小X关闭DockableForm时,它会触发DockableForm的OnClose事件,在OnClose事件中把LeftDockPanel的宽度设为0就行了。

procedure TDockableForm.FormClose(Sender: TObject;
 var Action: TCloseAction);
begin
 MainForm.LeftDockPanel.Width := 0;
 Action := caHide;
end;

以上所讲的是如何在主窗口上停靠窗体,原代码都通过测试。同理,我们可以在主窗口的右边,下边,上边都实现停靠功能。

三、总结:

在Delphi中只要是从TWinControl继承的控件都支持被停靠(如上面的LeftDockPanel),也就是有DockSite这个属性;所有从TControl继承的控件都支持停靠(如上面的DockableForm),也就是有DragKind这个属性.所以支持被停靠的控件都支持停靠,支持停靠的控件不一定支持被停靠,道理很简单,因为TWinControl继承于TControl。OnDockOver事件是控制停靠窗体的预览位置;OnDockDrap事件是控制停靠窗体的最终位置;OnGetSiteInfo是询问是否可以停靠;OnStartDock是停靠开始,OnEndDock是停靠结尾,OnUnDock是不停靠(也就是被拖出来时)。

一、基础知识介绍

1、CM_DOCKCLIENT消息和TCMDockClient结构,CM_DOCKCLIENT消息和TCMDockClient结构是相互对应的。

1>、TCMDockClient的结构是:

TCMDockClient = packed record
  Msg: Cardinal;
  DockSource: TDragDockObject;
  MousePos: TSmallPoint;
  Result: Integer;
 end;

参数说明:

  DockSource:包含了停靠—拖动操作的信息,其中有一个重要的属性是Control,另一个重要的属性是DockRect,就是停靠的位置;

  MousePos是:鼠标的位置。

2>、CM_DOCKCLIENT事件在停靠和被停靠控件都可以捕获,因为它是TWinControl类发出的。

procedure TWinControl.DockDrop(Source: TDragDockObject; X, Y: Integer);
begin
 if (Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0)
  and Assigned(FOnDockDrop) then
  FOnDockDrop(Self, Source, X, Y);
end;

从Delphi的DockDrop函数定义可以看出,TWinControl是先发送DOCKCLIENT消息,再触发OnDockDrop事件的。

2、用ManualDock函数可以安全的完成停靠功能,不要用Dock函数。

function ManualDock(NewDockSite: TWinControl; DropControl: TControl = nil; ControlSide: TAlign = alNone): Boolean;

参数说明:

  NewDockSite:要被停靠的窗体;

  DropControl:已经存在于NewDockSite的TControl,在这里可以把它设成nil;

  ControlSide: 停靠的位置,可以是上,下,左,右,全部等。

相应的还有一个:ManualFloat和UnDock函数

二、窗体之间相互停靠的实例

1.基本步骤:

1>、先创建一个新窗体:

设置属性Name:DockableForm;DockSite:True ;DragKind:dkDock;DragMode:dmAutomatic(自动停靠,表示当鼠标在工具条上点击并移动后,会自动发起拖放动作)。

DockSite必须设置为True,不然不会触发DOCKCLIENT消息处理函数,也就不能停靠了。

DragMode必须设置为dmAutomatic:不然停靠如宿主窗体之后,就不能再拖出来。

 

2>、再创建一个宿主窗体:

设置属性Name:TiledHost;DockSite:True;UseDockManager:True,表示窗体自动管理停靠。它的作用是用来装载两个DockableForm的。

注意:使用窗体作为停靠锚点时不会出现拖放把手和关闭按钮,而且停靠多个组件时,也不会自动排列,而是随意排列。所以,必须设置UseDockManager为True,使用一个停靠管理器来管理停靠的动作。

3>、在DockableForm中捕获DOCKCLIENT消息,在里面完成两个窗体的相互停靠

在private中声明消息处理函数及它的实现代码:

procedure CMDockClient(var Message: TCMDockClient); message CM_DOCKCLIENT;
end; procedure TDockableForm.CMDockClient(var Message: TCMDockClient);
var
 Host: TForm;
begin
 if Message.DockSource.Control is TDockableForm then
 begin
Host := TTiledHost.Create(Application);// 创建一个窗体实例
  Host.BoundsRect := Self.BoundsRect;// 获取组件所有角的像素位置,设置宿主窗体大小
  // 完成窗体的相互停靠   Self.ManualDock(Host, nil, alNone); // 把自己停靠到TTiledHost
  Self.DockSite := False;// 不允许其他控件停靠在自己上面
// 把Message.DockSource.Control也停靠到TTiledHost,完成窗体的相互停靠 Message.DockSource.Control.ManualDock(Host, nil, alNone);
  TDockableForm(Message.DockSource.Control).DockSite := False;
   Host.Visible := True;//必须添加,不然窗体全部消失不见  end;
end;

先解释一下上面的代码,首先创建TTiledHost的实例,然后用ManualDock函数把自己停靠到TTiledHost,把Message.DockSource.Control也停靠到TTiledHost,这样就完成了窗体的相互停靠,

4>、就在DockableForm的OnDockOver事件里加入代码,使程序产生停靠的预览效果。

procedure TDockableForm.FormDockOver(Sender: TObject;
 Source: TDragDockObject; X, Y: Integer; State: TDragState;
 var Accept: Boolean);
var
 ARect: TRect;
begin
 Accept := Source.Control is TDockableForm;
 if Accept then
 begin
  ARect.TopLeft := ClientToScreen(Point(0, 0));
  ARect.BottomRight := ClientToScreen(
   Point(ClientWidth div 2, ClientHeight));
  Source.DockRect := ARect;
 end;
end;

5>、在DockableForm中添加一个按钮创建新的DockableForm。

Var i: integer;

procedure TDockableForm.btn1Click(Sender: TObject);

var

  AForm: TDockableForm;

begin

  AForm := TDockableForm.Create(Application);

  AForm.Caption := 'AForm' + IntToStr(i);

  Inc(i);

  AForm.Show;

end;

6>、运行程序

点击按钮生成新的DockableForm时,把其中一个拖动到另一个上,就可以实现两个窗体之间的相互停靠,并一起在HostHost中显示。

因为TPageControl组件重载了TWinControl组件的DoAddDockClient和DoRemoveDockClient方法,能过自动响应停靠动作添加新的也没,而当浮动被停靠的窗口后将自动的将先前创建的TTabSheet也没删除,我们不需要写代码,只要设定基本的属性就可以实现分页停靠的功能。

一、两个窗体停靠成PageControl样式的实例

1.基本步骤:

1>、先创建一个新窗体(DockableForm):

设置属性Name:DockableForm;DockSite:True ;DragKind:dkDock;DragMode:dmAutomatic(自动停靠,表示当鼠标在工具条上点击并移动后,会自动发起拖放动作)。

DockSite必须设置为True,不然不会触发DOCKCLIENT消息处理函数,也就不能停靠了。

DragMode必须设置为dmAutomatic:不然停靠如宿主窗体之后,就不能再拖出来。

 

2>、再创建一个宿主窗体(TabHost):

设置属性Name:TabHost;DragKind:dkDock;在它上面放一个PageControl,设置属性Name: PageControl1;Align:alClient;DockSite:True;用于装载两个DockableForm.

说明:把宿主窗体的DragKind设置为dkDock是为了让宿主窗体也能停靠在其他可以停靠的锚点上。

3>、在DockableForm中捕获DOCKCLIENT消息,在里面完成两个窗体的相互停靠

在private中声明消息处理函数及它的实现代码:

procedure CMDockClient(var Message: TCMDockClient); message CM_DOCKCLIENT;
end; procedure TDockableForm.CMDockClient(var Message: TCMDockClient);
var
 Host: TForm;
begin
 if Message.DockSource.Control is TDockableForm then
 begin
   Host := TTabHost.Create(Application);
   Host.BoundsRect := Self.BoundsRect;
   Self.ManualDock(TTabHost(Host).PageControl1, nil, alClient);
  Message.DockSource.Control.ManualDock(TTabHost(Host).PageControl1, nil, alClient);
   Host.Visible := True;
   End;
End;

让TabHost具有停靠和被停靠的功能。还需要说明一下,TPageControl封装了一些对停靠的支持,它捕获了CM_DOCKCLIENT,CM_DOCKNOTIFICATION,CM_UNDOCKCLIENT,WM_LBUTTONDBLCLK消息处理停靠动作。具体可以查看TPageControl的原代码。

4>、就在DockableForm的OnDockOver事件里加入代码,使程序产生停靠的预览效果。

procedure TDockableForm.FormDockOver(Sender: TObject;
 Source: TDragDockObject; X, Y: Integer; State: TDragState;
 var Accept: Boolean);
var
 ARect: TRect;
begin
 Accept := Source.Control is TDockableForm;
 if Accept then
 begin
  ARect.TopLeft := ClientToScreen(ClientRect.TopLeft);
  ARect.BottomRight := ClientToScreen(ClientRect.BottomRight);
  Source.DockRect := ARect;
 end;

5>、在DockableForm中添加一个按钮创建新的DockableForm。

Var i: integer;

procedure TDockableForm.btn1Click(Sender: TObject);

var

  AForm: TDockableForm;

begin

  AForm := TDockableForm.Create(Application);

  AForm.Caption := 'AForm' + IntToStr(i);

  Inc(i);

  AForm.Show;

end;

6>、运行程序

点击按钮生成新的DockableForm时,把其中一个拖动到另一个上,就可以实现两个窗体一起停靠在TabHost中的PageControl上显示。就可以看到每停靠一个新窗体,PageControl就会新建一个页面,每浮动一个窗体,就会删除先前的页面。但是浮动完所有的窗体时,宿主窗体并不自动关闭。所以,可以在宿主窗体中添加代码控制。

二、总结:

工具条的停靠也一样,在主窗体上放一个ControlBar或CoolBar,把他们的DockSite设成True;再在上面放ToolBar, ToolBar的DragKind属性设成dkDock,DragMode属性设为dmAutomatic。在这里,TControl有一个属性叫FloatingDockSiteClass,它的类型是TWinControl的引用(class of TWinControl),只要在主窗口创建时,把ToolBar的FloatingDockSiteClass属性设成某一个窗体A,比如在设计时A这个窗体叫ToolBarDockForm,但在程序里面不用显式的创建A,Delphi会自动创建,当ToolBar被拖动出来时,Delphi自动把它装载到ToolBarDockForm里,当然ToolBarDockForm也要象上面提到的DockableForm一样设置一定的属性和添加一些代码。

1、停靠时Delphi会产生的方法和事件

TControl.ManualFloat      // 和ManualDock相对应,使浮动。
TControl.ReplaceDockedControl  // 替换停靠控件
TWinControl .DoAddDockClient
TWinControl .DockDrop
TWinControl .DockOver
TWinControl .DoDockOver
TWinControl .DoUnDock

Delphi 停靠技术的应用的更多相关文章

  1. Delphi 停靠技术的应用3(两个窗体停靠成PageControl样式, 分页停靠)

    Delphi 停靠技术的应用3(两个窗体停靠成PageControl样式, 分页停靠) 因为TPageControl组件重载了TWinControl组件的DoAddDockClient和DoRemov ...

  2. delphi 数据库技术沉浮录--谨给成为历史的BDE

    2014年9月,delphi xe7 出来了,这次在数据库技术方面,彻底抛掉了从1995 年 delphi 1.0 就自带的(Borland Database Engine)数据库访问技术.从而宣告了 ...

  3. Delphi中停靠技术的实现

    随着软件技术的不断进步,软件界面也越来越美观,操作也越来越方便.综观市面上比较专业的各种软件,我们会发现大部分都提供窗体停靠的功能,特别象工具软件,基本上都或多或少有停靠功能.自然,Delphi也支持 ...

  4. 停靠技术 Dock

    C:\Program Files\Borland\Delphi7\Demos\Docking   delphi例子 网上文档 http://www.docin.com/p-95543759.html

  5. Delphi数据库技术中Disablecontrols和Enablecontrols的功能

    一般来说,用来扫描整个数据库表并修改每个记录的某一个字段的程序如下所示: with Table Do begin DisableControls;{在修改记录的过程中,使其它部件无效} First; ...

  6. 如何在Exe和BPL插件中实现公共变量共享及窗口溶入技术Demo源码

    如何在Exe和BPL插件中实现公共变量共享及窗口溶入技术Demo源码 1.Delphi编译方式介绍: 当我们在开发一个常规应用程序时,Delphi可以让我们用两种方式使用VCL,一种是把VCL中的申明 ...

  7. delphi DockPresident

    作为Delphi的忠实用户,我想大家对Delphi中的停靠窗体应该比较熟悉吧,是不是也希望自己编的程序也具有这样的功能?使她看起来更漂亮,更专业,更方便. 本人做的一套停靠控件DockPresiden ...

  8. Delphi xe5 手机开发经验(新手级别)

    Delphi xe5 手机开发经验(新手级别) http://diybbs.zol.com.cn/1/34037_699.html http://www.delphitop.com/html/jiqi ...

  9. 吐血整理 Delphi系列书籍 118本(全)

    Delphi 教程 系列书籍 网友(老帅)整理 001_<Delhpi6数据库设计思想与实践> 002_<Delphi6应用开发指南> 003_<Delphi6开发人员指 ...

随机推荐

  1. Unity3D中Prefab

    Prefab概念: Prefab是一种资源类型--存储在项目视图中的一种可反复使用的游戏对象.因而当游戏中须要非常多反复使用的对象.资源等时,Prefab就有了用武之地.它拥有下面特点: 能够放到多个 ...

  2. LeetCode刷题笔录Add Binary

    Given two binary strings, return their sum (also a binary string). For example, a = "11" b ...

  3. PureMVC(JS版)源码解析(五):SimpleCommand类

          之前我们对PureMVC中涉及到观察者模式的三个基本类(Notification/Observer/Notifier)进行了分析,接下来将对PureMVC源码中的其他类进行分析,首先我们讲 ...

  4. jdk1.5多线程Lock接口及Condition接口

    jdk1.5多线程的实现的方式: jdk1.5之前对锁的操作是隐式的 synchronized(对象) //获取锁 { } //释放锁 jdk1.5锁的操作是显示的:在包java.util.concu ...

  5. XC软件管理器应用

    这是一个基于android 4.4开发的android应用-XC软件管理器.包含应用的信息查看,打开应用以及应用的卸载等功能.非常实用的一个应用,欢迎大家下载使用. 下载地址:http://downl ...

  6. Java基础知识强化之集合框架笔记51:Map集合之Map集合的功能概述与测试

    1. Map集合的功能概述 (1)添加功能 V put(K key,V value):添加元素.这个其实还有另一个功能?先不告诉你,等会讲 如果键是第一次存储,就直接存储元素,返回null 如果键不是 ...

  7. CSS hack常用方案(摘选)

    邮箱因为默认了line-height?:170%,导致采用table元素时继承问题,可以采用line-height:50% 很好解决. 常 在使用float时,后面的显示不正常,因为继承了float了 ...

  8. JavaScript与html5写的贪吃蛇完整代码

    JavaScript与html5写的贪吃蛇完整代码 查看运行效果可访问http://www.codesocang.com/texiao/youxitexiao/2014/0402/7045.html# ...

  9. springMVC+freemarker中Could not resolve view with name... 问题解决

    用到springMVC+freemarker,自己在做demo过程中报: 严重: Servlet.service() for servlet springmvc threw exception jav ...

  10. easyui-treegrid移除树节点出错

    easyui-treegrid移除树节点出错 >>>>>>>>>>>>>>>>>>>& ...