{******************************************************************************} { CnPack For Delphi/C++Builder } { 中国人自己的开放源码第三方开发包 } { (C)Copyright 2001-2018 CnPack 开发组 } { ------------------------------------ } { } { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 } { 改和重新发布这一程序。 } { } { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 } { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 } { } { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 } { 还没有,可访问我们的网站: } { } { 网站地址:http://www.cnpack.org } { 电子邮件:master@cnpack.org } { } {******************************************************************************} {*******************************************************} { } { 停靠服务和客户控件 } { CnDockFormControl 单元 } { } { 版权 (C) 2002,2003 鲁小班 } { } {*******************************************************} unit CnDockFormControl; {* |
================================================================================
* 软件名称:不可视工具组件包停靠单元
* 单元名称:停靠服务和客户控件单元 
* 单元作者:CnPack开发组 周益波(鲁小班)
* 备    注:本单元由原作者授权CnPack开发组移植,已保留原作者版权信息
* 开发平台:
* 兼容测试:PWin9X/2000/XP + Delphi 5/6/7
* 本 地 化:该单元中的字符串均符合本地化处理方式
* 单元标识:$Id$
* 修改记录:2007.07.13 V1.0
*                移植单元
================================================================================
|
} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.00 作者: 周益波 E-mail: zhouyibo2000@sina.com QQ: 51571811 开始时间 -- 2002-01-14 结束时间 -- 2002-01-20 主要功能: 使窗体具有停靠功能,并且管理停靠窗体。 如何使用: 在您的主窗体上放一个TCnDockServer,在一般的工具窗体上放一个TCnDockClient 控件,这样,工具窗体就可以停靠到主窗体上了,可以停靠到主窗体的上下左右 四个方向上。同时,两个工具窗体还有相互停靠的功能,它们可以停靠成平铺型和 分页型的窗体。 CnDockFormControl单元还提供了一些函数和过程来操作停靠窗体,它们的定义见下面的 注释。 在下面的代码中,还定义了两个类的引用,这样,用户就可以用自定义的类替换掉原先 的类,改变她们的默认特性。 BUG-- 1.在进行停靠操作时,停靠窗体的第一个Windows标准控件里面的内容有可能被清空, 2.停靠窗体的Caption有可能被清空。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.01 结束时间 -- 2002-02-09 增加了保存停靠信息和还原停靠信息的功能, 用户不但可以把停靠信息存储在INI文件中,还可以保存在注册表中。 因为在还原停靠信息的时候把桌面锁住了,所以不会产生闪烁现象。 增加了对停靠选项的处理函数 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.02 结束时间 -- 2002-02-20 增加了辅助控件TCnBasicDockStyle,用来改变停靠的风格,如: 停靠的预览效果,停靠后把手的外观等。 而且用户可以继承TCnBasicDockStyle,用来自定义外观,提供最大的灵活性 用户可以分别继承TCnDockSplitter,TCnDockPanel,TCnConjoinPanel, TCnTabPageControl,TCnDockZone和TCnDockTree类来改变停靠风格。 目前增加的停靠风格类是CnDelphiDockStyle和CnVCDockStyle, 她们分别表示类似Delphi和类似VC++的风格。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.03 结束时间 -- 2002-03-10 修正了0.00版中的两个BUG,程序运行的更加稳定、可靠了。 增加了TCnVIDDockStyle类,她是模仿Visual InderDev的停靠的风格,还没有完全完成, +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.031 结束时间 -- 2002-03-17 完成了一个很有难度的功能,就是当停靠控件是TCnConjoinDockHostForm的时候, 就把它上面的TCnConjoinPanel的停靠信息保存起来,然后再把它还原到DropOnControl中。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.04 结束时间 -- 2002-04-17 对程序的底层代码进行了大幅度的修改,现在,这套控件已经不再受Delphi的VCL类库的 限制了,我们完全可以根据自己的意愿来设置停靠的风格了。 其中增加了TCnDockPresident类,这个类用于当鼠标拖动控件的时候处理停靠事件。 又增加了TCnCustomDockControl类,这个类从TCustomControl继承过来,在她里面定义 了一些和停靠相关的函数和方法。后面的TCnDockPanel, TCnConjoinPanel和 TCnTabPageControl都从TCnCustomDockControl继承。这样,只要把她们三个类中相同的 代码都放在TCnCustomDockControl中,就起到了代码重用的作用;还有,当检测这个类 的级别的时候也很方便。由于代码大幅度的修改,暂时停靠风格类还不能完全使用, 不过应该马上可以修改好的。关于停靠风格,以后还要增加: TCnWPSDockStyle(类似WPS的停靠风格), TCnPhotoShopDockStyle(类似PhotoShop的停靠风格), TCnTancentDockStyle(类似腾讯浏览器的停靠风格), TCnOfficeDockStyle(类似Office XP的停靠风格), TCnNETDockStyle(类似.NET的停靠风格), TCnVBDockStyle(类似Visual Basic的停靠风格), TCnCustomDockStyle(自定义的停靠风格)。 ... +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.041 结束时间 -- 2002-04-24 增加了一些TCnDockClient中的属性,如: DirectDrag(当鼠标点击标题栏的时候是否立刻进行停靠操作), NCPopupMenu(当鼠标右键点击标题栏的时候是否产生弹出菜单), ShowHint(当鼠标移动到标题栏上的时候是否显示提示信息)。 又增加了一些TCnDockClient的事件,如: OnFormShowHint(当提示信息显示的时候触发) OnNCButtonDown(当鼠标在标题栏上按下的时候触发) OnNCButtonUp(当鼠标在标题栏上释放的时候触发) OnNCMouseMove(当鼠标在标题栏上移动的时候触发) OnNCButtonDblClk(当鼠标在标题栏上双击的时候触发) OnPaintDockGrabber(当重画把手的时候触发) OnPaintDockSplitter(当重画分割条的时候触发) 对TCnVCDockStyle类进行了一些改进,使它看上去更象VC的停靠风格,不过有些地方 还有待改进,这只是时间问题。 现在这套控件看上去功能越来越强大了,嘻嘻,不过我还不急着发布她,希望把她做地完美 无缺后再发布。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.041 结束时间 -- 2002-05-10 对TCnVCDockStyle类进行了进一步的改进,现在她的特性已经很类似于VC++的停靠风格了。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.042 结束时间 -- 2002-05-24 做了一个类似Visual C++界面的程序,用来演示TCnVCDockStyle控件,做的很酷哦. +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.050 结束时间 -- 2002-06-13 初步完成了Visual InterDev中的停靠分页的界面。而且有一个副产物TCnVIDTabPageControl 控件,可以作为一般的分页控件来使用。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.051 结束时间 -- 2002-07-03 基本上完成了Visual InterDev中的停靠分页的界面。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.060 结束时间 -- 2002-08-11 已经完成Visual InterDev中的停靠分页的界面,在TCnBasicDockStyle类中增加了两个 属性:TCnBasicConjoinServerOption和TCnBasicTabServerOption,用户可以继承这 两个类来实现自己的停靠风格的属性。 并且制作了一个帮助文件,接下去就开始测试了,准备在十月份发布这套控件。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.065 结束时间 -- 2002-08-20 找到了一些BUG,并且已经改正,继续测试,可靠性是软件的根本。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.070 结束时间 -- 2002-09-14 增加了一个很重要的功能,就是当用户关闭一个已经停靠了的窗体后,可以把这个窗体 在停靠服务器中的原来的位置保存下来,用户再次显示它的时候,可以还原到原来的位置 和大小,这个功能在Visual C++和Visual InterDev中都有,可是在Delphi中没有, 现在我把这个功能也加到CnDelphiDockStyle中了。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.100 结束时间 -- 2002-11-20 程序的试用版已经发布了,反响还不错,用户发现了一些Bug,现在已经修改了。 后来又增加了一个类似Visual InterDev的Demo,本来想增加一个双击标题栏还原的 功能,结果改啊改,但是最后发现改不下去了,为什么?说出来丢脸,由于刚开始做 这个控件的时候没有想到会做成象现在功能这么强大,程序结构没有设计好,到后来 发觉再改下去的话,程序就会发展到不可控制的混乱程度。最后,只能忍痛割爱,暂时 把双击还原的功能去掉,然后初步决定等到做完了类似VS.NET的停靠风格后就发布正式 版,所以这几天正忙着做类似VS.NET的停靠风格TCnVSNETDockStyle呢。大概能到12月中 能做完,然后还要测试,恐怕只能等到2003年1月份才能发布了。 等正式版发布的时候,我还要把帮助文件翻译成English,让老外也来用我们中国人的 控件,但是我还不知道是作为免费软件还是共享软件发布,到时候再说吧。 本人初步打算把DockPresident移植到C#上面,不过不知道能在什么时候开始,因为我 对C#也是一知半解,前一阵子学了一下,现在又放下了,唉,没时间啊。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.0110 结束时间 -- 2002-12-14 初步完成了类似VS.NET停靠风格的类TCnVSNETDockStyle,功能基本上和VS.NET停靠风格 差不多,不过由于Delphi的特殊性,有些地方没有办法和VS.NET停靠风格完全一样,如果 读者想了解VS.NET的停靠风格,请参看《VS.NET的停靠风格》。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.0112 结束时间 -- 2002-12-20 做了一个类似MSDN2002停靠风格的Demo,效果还不错,呵呵。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.0120 结束时间 -- 2003-1-4 新版本的控件已经基本上完成,现在进入测试阶段,预计春节后发布。接下去的时间主要 是作测试,并且修改一些细节的东东,完善帮助,补全文档等等。 程序的代码量已经突破25000行,如果去掉注释部分应该也有20000多行,也算是中等规模 的控件了,呵呵,足足花了我一年的业余时间,中途也曾经打过退堂鼓,但还是咬牙坚持 了下来,不管这个控件以后的命运会怎么样,能够完成她其实已经是一种胜利了,呵呵。 接下去的任务就是要把她养大,花点时间来推广她,如果哪位朋友觉得Dock President 还不错,就请向您的朋友推荐一下,在这里鲁小班先向您表示感谢了。 +------------------------------------------------------------------------------} {------------------------------------------------------------------------------+ CnDockFormControl -- 版本 0.0121 结束时间 -- 2003-2-16 改正了一个问题,那就是在WinXP下面装载停靠信息的时候,屏幕会闪烁,其实这个 问题在98或者2000以下是没有的,因为程序已经调用了LockWindowUpdate函数, 但是在XP下面不知道是什么原因,还是会出现这种闪烁的情况,后来没办法,只能在 装载的时候过滤掉CM_SHOWINGCHANGED消息,然后在装载的最后把应该显示但是没有 显示的客户窗体显示出来。 +------------------------------------------------------------------------------} {$P+,S-,W-,R-,T-,H+,X+} interface {$I CnPack.inc} uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Menus, Extctrls, Comctrls, Stdctrls, Consts, Inifiles, CommCtrl, Registry, CnDockTree, CnDockSupportClass, CnDockSupportControl, CnClasses, CnConsts, CnCompConsts; const { 停靠的状态 } DS_Unknow = 0; // 不知道是什么状态 DS_Docking = 1; // 停靠状态 DS_Floating = 2; // 浮动状态 type TCnSplitterSize = 0..32767; TCnDockBaseControl = class; TCnDockServer = class; TCnDockClient = class; TCnConjoinPanel = class; TCnTabPageControl = class; TCnConjoinDockHostForm = class; TCnTabDockHostForm = class; { 用来在停靠服务端中调整客户端的分割条 } TCnDockSplitter = class({TSplitter)//}TCustomDockPanelSplitter) private { 表示被哪个TDockServer拥有 } FDockServer: TCnDockServer; function GetSplitterIndex: Integer; protected function FindControl: TControl; override; property DockServer: TCnDockServer read FDockServer write FDockServer; public constructor Create(AOwner: TComponent); override; property SplitterIndex: Integer read GetSplitterIndex; end; // 当调用ShowDockPanel函数设置DockPanel的宽度或者高度的时候, // 用来指定到底是按照DockPanel本身的TBDockHeight和LRDockWidth // 还是按照Client的TBDockHeight和LRDockWidth来设置。 TSetDockPanelSizeFrom = (sdfDockPanel, sdfClient); { 停靠服务端中用来停靠客户端的Panel } TCnDockPanel = class(TCnCustomDockPanel) private { 表示被哪个TDockServer拥有 } FDockServer: TCnDockServer; function GetPanelIndex: Integer; protected procedure SetDockServer(const Value: TCnDockServer); virtual; procedure ReloadDockedControl(const AControlName: string; var AControl: TControl); override; { ------------------------------------------------------------------------ } procedure CustomStartDock(var Source: TCnDragDockObject); override; procedure CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); override; procedure CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override; procedure CustomPositionDockRect(Source: TCnDragDockObject; X, Y: Integer); override; procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override; procedure CustomEndDock(Target: TObject; X, Y: Integer); override; function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override; { ------------------------------------------------------------------------ } function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override; { ------------------------------------------------------------------------ } procedure Resize; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override; { 显示停靠的窗体 } procedure ShowDockPanel(MakeVisible: Boolean; Client: TControl; PanelSizeFrom: TSetDockPanelSizeFrom = sdfClient); virtual; procedure ResetPosition; property PanelIndex: Integer read GetPanelIndex; property DockServer: TCnDockServer read FDockServer write SetDockServer; end; TCnAdvDockPanel = class(TCnDockPanel) private procedure CMUnDockClient(var Message: TCMUnDockClient); message CM_UNDOCKCLIENT; protected procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override; function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override; { ------------------------------------------------------------------------ } function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override; public procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override; end; TCnDockPanelClass = class of TCnDockPanel; TCnDockSplitterClass = class of TCnDockSplitter; TCnConjoinPanelClass = class of TCnConjoinPanel; TCnTabDockClass = class of TCnTabPageControl; { 分割条的风格 } TCnSplitterStyle = class(TPersistent) private FSplitter: TCnDockSplitter; FDockServer: TCnDockServer; FColor: TColor; FCursor: TCursor; FParentColor: Boolean; FResizeStyle: TResizeStyle; FSize: TCnSplitterSize; // 大小,当是[左,右]时就是宽度,当是[上,下]时就是高度。 FMinSize: TCnSplitterSize; // 距离父控件最小的位置 procedure SetColor(const Value: TColor); procedure SetCursor(const Value: TCursor); procedure SetParentColor(const Value: Boolean); procedure SetResizeStyle(const Value: TResizeStyle); procedure SetSize(const Value: TCnSplitterSize); procedure SetMinSize(const Value: TCnSplitterSize); protected procedure AssignTo(Dest: TPersistent); override; procedure AssignToSplitter(Dest: TCnDockSplitter); procedure SetSplitterStyle; property Splitter: TCnDockSplitter read FSplitter write FSplitter; public constructor Create(ASplitter: TCnDockSplitter; ACursor: TCursor); virtual; procedure Assign(Source: TPersistent); override; published property Color: TColor read FColor write SetColor default clBtnFace; property Cursor: TCursor read FCursor write SetCursor; property ParentColor: Boolean read FParentColor write SetParentColor default True; property ResizeStyle: TResizeStyle read FResizeStyle write SetResizeStyle default rsPattern; property Size: TCnSplitterSize read FSize write SetSize default 3; property MinSize: TCnSplitterSize read FMinSize write SetMinSize default 30; end; TCnBasicDockStyle = class; { TCnBasicConjoinServerOption和TCnBasicTabServerOption共同的父类 } TCnBasicServerOption = class(TPersistent) private FDockStyle: TCnBasicDockStyle; protected { 重新设置和DockStyle有联系的TCnDockBaseControl的配置 } procedure ResetDockControlOption; virtual; abstract; { 重新设置ADockServer的配置 } procedure ResetDockServerOption(ADockServer: TCnDockServer); virtual;// abstract; { 重新设置ADockClient的配置 } procedure ResetDockClientOption(ADockClient: TCnDockClient); virtual;// abstract; property DockStyle: TCnBasicDockStyle read FDockStyle write FDockStyle; public constructor Create(ADockStyle: TCnBasicDockStyle); virtual; destructor Destroy; override; procedure Assign(Source: TPersistent); override; end; TGrabbersSize = 1..MaxInt; TSplitterWidth = 1..MaxInt; { 平铺服务器的选项类 } TCnBasicConjoinServerOption = class(TCnBasicServerOption) private FGrabbersSize: TGrabbersSize; FSplitterWidth: TSplitterWidth; protected procedure SetGrabbersSize(const Value: TGrabbersSize); virtual; procedure SetSplitterWidth(const Value: TSplitterWidth); virtual; { ------------------------------------------------------------------------ } procedure ResetDockControlOption; override; procedure ResetDockServerOption(ADockServer: TCnDockServer); override; procedure ResetDockClientOption(ADockClient: TCnDockClient); override; { 重新设置TCnDockPanel的属性 } procedure ResetDockPanel(APanel: TCnDockPanel); virtual; { 重新设置TCnConjoinPanel的属性 } procedure ResetConjoinPanel(APanel: TCnConjoinPanel); virtual; public constructor Create(ADockStyle: TCnBasicDockStyle); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; procedure SetGrabbersSize_WithoutChangeSystemInfo(const Value: TGrabbersSize); procedure SetSplitterWidth_WithoutChangeSystemInfo(const Value: TSplitterWidth); published property GrabbersSize: TGrabbersSize read FGrabbersSize write SetGrabbersSize; property SplitterWidth: TSplitterWidth read FSplitterWidth write SetSplitterWidth; end; { 分页服务器的选项类 } TCnBasicTabServerOption = class(TCnBasicServerOption) private FTabPosition: TTabPosition; FHotTrack: Boolean; protected procedure SetTabPosition(const Value: TTabPosition); virtual; procedure SetHotTrack(const Value: Boolean); virtual; { ------------------------------------------------------------------------ } procedure ResetDockControlOption; override; procedure ResetDockServerOption(ADockServer: TCnDockServer); override; procedure ResetDockClientOption(ADockClient: TCnDockClient); override; { 重新设置TCnTabPageControl的属性 } procedure ResetTabPageControl(APage: TCnTabPageControl); virtual; public constructor Create(ADockStyle: TCnBasicDockStyle); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; published property HotTrack: Boolean read FHotTrack write SetHotTrack default False; property TabPosition: TTabPosition read FTabPosition write SetTabPosition default tpTop; end; TCnBasicConjoinServerOptionClass = class of TCnBasicConjoinServerOption; TCnBasicTabServerOptionClass = class of TCnBasicTabServerOption; { 所有停靠风格的基类 } TCnBasicDockStyle = class(TCnComponent) private FCnDockPanelClass: TCnDockPanelClass; // TCnDockServer中四个TCnDockPanel的类的引用 FCnDockSplitterClass: TCnDockSplitterClass;// TCnDockServer中四个TSplitter的类的引用 FCnConjoinPanelClass: TCnConjoinPanelClass;// 平铺服务器的类的引用 FCnTabDockClass: TCnTabDockClass; // 分页服务器的类的引用 FCnDockPanelTreeClass: TCnDockTreeClass; // TCnDockPanel中IDockManager的类的引用 FCnDockPanelZoneClass: TCnDockZoneClass; // TCnDockPanel中IDockManager的节点类的引用 FCnConjoinPanelTreeClass: TCnDockTreeClass;// TCnConjoinPanel中IDockManager的类的引用 FCnConjoinPanelZoneClass: TCnDockZoneClass; FCnConjoinServerOptionClass: TCnBasicConjoinServerOptionClass; FCnTabServerOptionClass: TCnBasicTabServerOptionClass; FCnConjoinServerOption: TCnBasicConjoinServerOption; FCnTabServerOption: TCnBasicTabServerOption; FParentForm: TForm; // 父窗体 FOldWindowProc: TWndMethod; { 一个列表,用来存储所有引用了TCnBasicDockStyle对象的TCnDockBaseControl对象 } FDockBaseControlList: TList; function GetDockBaseControlListCount: Integer; function GetDockBaseControlLists(Index: Integer): TCnDockBaseControl; protected procedure GetComponentInfo(var AName, Author, Email, Comment: string); override; procedure FormStartDock(DockClient: TCnDockClient; var Source: TCnDragDockObject); virtual; procedure FormGetSiteInfo(Source: TCnDragDockObject; DockClient: TCnDockClient; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); virtual; procedure FormDockOver(DockClient: TCnDockClient; Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); virtual; procedure FormPositionDockRect(DockClient: TCnDockClient; Source: TCnDragDockObject); virtual; procedure FormDockDrop(DockClient: TCnDockClient; Source: TCnDragDockObject; X, Y: Integer); virtual; procedure FormEndDock(DockClient: TCnDockClient; Target: TObject; X, Y: Integer); virtual; function FormUnDock(DockClient: TCnDockClient; NewTarget: TWinControl; Client: TControl): Boolean; virtual; procedure FormGetDockEdge(DockClient: TCnDockClient; Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); virtual; { ------------------------------------------------------------------------ } { 创建平铺停靠分割的选项类 } procedure CreateConjoinServerOption(var Option: TCnBasicConjoinServerOption); virtual; { 创建分页停靠分割的选项类 } procedure CreateTabServerOption(var Option: TCnBasicTabServerOption); virtual; { 创建平铺和分页停靠分割的选项类 } procedure CreateServerOption; virtual; { 释放平铺和分页停靠分割的选项类 } procedure FreeServerOption; virtual; { ------------------------------------------------------------------------ } procedure SetDockBaseControl(IsCreate: Boolean; DockBaseControl: TCnDockBaseControl); virtual; { 捕获TCnDockServer的WindowProc消息,如果要还要执行默认的消息处理就返回False,否则就返回True } function DockServerWindowProc(DockServer: TCnDockServer; var Message: TMessage): Boolean; virtual; { 捕获TCnDockClient的WindowProc消息,如果要还要执行默认的消息处理就返回False,否则就返回True } function DockClientWindowProc(DockClient: TCnDockClient; var Message: TMessage): Boolean; virtual; { 捕获父控件的WindowProc消息 } procedure ParentFormWindowProc(var Message: TMessage); virtual; { 把ADockBaseControl添加到FDockBaseControlList中, 如果已经存在了就不插入,反之插入到列表的结尾处 } procedure AddDockBaseControl(ADockBaseControl: TCnDockBaseControl); virtual; { 把ADockBaseControl从FDockBaseControlList中删除, 如果不存在了就不删除,反之删除 } procedure RemoveDockBaseControl(ADockBaseControl: TCnDockBaseControl); virtual; { ------------------------------------------------------------------------ } procedure SetConjoinServerOption( const Value: TCnBasicConjoinServerOption); virtual; procedure SetTabServerOption(const Value: TCnBasicTabServerOption); virtual; function GetConjoinServerOption: TCnBasicConjoinServerOption; virtual; function GetTabServerOption: TCnBasicTabServerOption; virtual; { 给当前的APanel的平铺的风格赋值,子类可以重载这个方法添加新的属性 } procedure AssignConjoinServerOption(APanel: TCnCustomDockPanel); virtual; { 给当前的APage的分页的风格赋值,子类可以重载这个方法添加新的属性 } procedure AssignTabServerOption(APage: TCnTabPageControl); virtual; procedure Loaded; override; property DockBaseControlList: TList read FDockBaseControlList; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure AfterConstruction; override; function CanSetEnableDocked(ADockBaseControl: TCnDockBaseControl): Boolean; virtual; function CanSetLeftDocked(ADockBaseControl: TCnDockBaseControl): Boolean; virtual; function CanSetRightDocked(ADockBaseControl: TCnDockBaseControl): Boolean; virtual; function CanSetTopDocked(ADockBaseControl: TCnDockBaseControl): Boolean; virtual; function CanSetBottomDocked(ADockBaseControl: TCnDockBaseControl): Boolean; virtual; function CanSetEachOtherDocked(ADockBaseControl: TCnDockBaseControl): Boolean; virtual; function GetControlName: string; virtual; function GetDockStyleVersion: string; virtual; { 重新设置光标的形状 } procedure ResetCursor(Source: TCnDragDockObject); virtual; { 获得DockClient参数的停靠状态 } function GetDockState(DockClient: TCnDockClient): Integer; virtual; procedure ResetDockControlConjoinOption; procedure ResetDockControlTabOption; { ------------------------------------------------------------------------ } procedure ShowDockForm(ADockClient: TCnDockClient); virtual;// 显示ADockClient中的ParentForm; procedure HideDockForm(ADockClient: TCnDockClient); virtual;// 隐藏ADockClient中的ParentForm; function GetDockFormVisible(ADockClient: TCnDockClient): Boolean; virtual;// 得到ADockClient中的ParentForm是否可见. { ------------------------------------------------------------------------ } property DockBaseControlListCount: Integer read GetDockBaseControlListCount; property DockBaseControlLists[Index: Integer]: TCnDockBaseControl read GetDockBaseControlLists; { 还原原先的客户的状态 } procedure RestoreClient(DockClient: TCnDockClient); virtual; { ------------------------------------------------------------------------ } property Version: string read GetDockStyleVersion;// 版本号 property ControlName: string read GetControlName; // 控件名称 { ------------------------------------------------------------------------ } property CnDockPanelClass: TCnDockPanelClass read FCnDockPanelClass write FCnDockPanelClass; property CnDockSplitterClass: TCnDockSplitterClass read FCnDockSplitterClass write FCnDockSplitterClass; property CnConjoinPanelClass: TCnConjoinPanelClass read FCnConjoinPanelClass write FCnConjoinPanelClass; property CnTabDockClass: TCnTabDockClass read FCnTabDockClass write FCnTabDockClass; property CnDockPanelTreeClass: TCnDockTreeClass read FCnDockPanelTreeClass write FCnDockPanelTreeClass; property CnDockPanelZoneClass: TCnDockZoneClass read FCnDockPanelZoneClass write FCnDockPanelZoneClass; property CnConjoinPanelTreeClass: TCnDockTreeClass read FCnConjoinPanelTreeClass write FCnConjoinPanelTreeClass; property CnConjoinPanelZoneClass: TCnDockZoneClass read FCnConjoinPanelZoneClass write FCnConjoinPanelZoneClass; { ------------------------------------------------------------------------ } property CnConjoinServerOptionClass: TCnBasicConjoinServerOptionClass read FCnConjoinServerOptionClass write FCnConjoinServerOptionClass; property CnTabServerOptionClass: TCnBasicTabServerOptionClass read FCnTabServerOptionClass write FCnTabServerOptionClass; { ------------------------------------------------------------------------ } property ParentForm: TForm read FParentForm write FParentForm; { ------------------------------------------------------------------------ } property ConjoinServerOption: TCnBasicConjoinServerOption read GetConjoinServerOption write SetConjoinServerOption; property TabServerOption: TCnBasicTabServerOption read GetTabServerOption write SetTabServerOption; end; TCnAdvDockStyle = class(TCnBasicDockStyle) protected function DockClientWindowProc(DockClient: TCnDockClient; var Message: TMessage): Boolean; override; end; { 停靠服务端和客户端共同的父类 } TCnDockBaseControl = class(TCnComponent) private FEnableDock, // 是否可以停靠在服务端 FLeftDock, // 是否可以停靠在服务端的左边 FTopDock, // 是否可以停靠在服务端的上边 FRightDock, // 是否可以停靠在服务端的右边 FBottomDock, // 是否可以停靠在服务端的下边 FEachOtherDock: Boolean;// 是否可以相互停靠 FDockStyle: TCnBasicDockStyle; FOldOnClose: TCloseEvent; FOldOnCreate: TNotifyEvent; FParentForm: TForm; FOldWindowProc: TWndMethod; protected procedure Loaded; override; procedure SetParentComponent(Value: TComponent); override; { ------------------------------------------------------------------------ } function CanSetEnableDocked: Boolean; virtual; function CanSetLeftDocked: Boolean; virtual; function CanSetRightDocked: Boolean; virtual; function CanSetTopDocked: Boolean; virtual; function CanSetBottomDocked: Boolean; virtual; function CanSetEachOtherDocked: Boolean; virtual; { ------------------------------------------------------------------------ } procedure DoFormOnClose(Sender: TObject; var Action: TCloseAction); virtual; procedure DoFormOnCreate(Sender: TObject); virtual; { ------------------------------------------------------------------------ } procedure SetBottomDock(const Value: Boolean); virtual; procedure SetEachotherDock(const Value: Boolean); virtual; procedure SetEnableDock(const Value: Boolean); virtual; procedure SetLeftDock(const Value: Boolean); virtual; procedure SetRightDock(const Value: Boolean); virtual; procedure SetTopDock(const Value: Boolean); virtual; procedure SetDockStyle(const Value: TCnBasicDockStyle); virtual; { ------------------------------------------------------------------------ } procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure WindowProc(var Message: TMessage); virtual; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Assign(Source: TPersistent); override; property ParentForm: TForm read FParentForm; { ------------------------------------------------------------------------ } // class function GetDockStyleVersion: string; virtual; function GetDockStyleVersion: string; virtual; { ------------------------------------------------------------------------ } property EnableDock: Boolean read FEnableDock write SetEnableDock default True; property LeftDock: Boolean read FLeftDock write SetLeftDock default True; property TopDock: Boolean read FTopDock write SetTopDock default True; property RightDock: Boolean read FRightDock write SetRightDock default True; property BottomDock: Boolean read FBottomDock write SetBottomDock default True; property EachOtherDock: Boolean read FEachotherDock write SetEachotherDock default True; { ------------------------------------------------------------------------ } property Version: string read GetDockStyleVersion; // 版本号 { ------------------------------------------------------------------------ } property DockStyle: TCnBasicDockStyle read FDockStyle write SetDockStyle; end; TGetClientAlignSizeEvent = procedure(Align: TAlign; var Value: Integer) of object; TFinishSetDockPanelSizeEvent = procedure(DockPanel: TCnDockPanel) of object; { 停靠服务端 } TCnDockServer = class(TCnDockBaseControl) private FDockPanelClass: TCnDockPanelClass; FLeftDockPanel, // 左边的停靠面板 FTopDockPanel, // 上边的停靠面板 FBottomDockPanel, // 下边的停靠面板 FRightDockPanel: TCnDockPanel; // 右边的停靠面板 FDockSplitterClass: TCnDockSplitterClass; FLeftSplitter, // 左边的分割条 FTopSplitter, // 上边的分割条 FBottomSplitter, // 下边的分割条 FRightSplitter: TCnDockSplitter; // 右边的分割条 FLeftSplitterStyle, // 左边的分割条风格 FTopSplitterStyle, // 上边的分割条风格 FRightSplitterStyle, // 下边的分割条风格 FBottomSplitterStyle: TCnSplitterStyle; // 右边的分割条风格 FOnGetClientAlignSize: TGetClientAlignSizeEvent; FOnFinishSetDockPanelSize: TFinishSetDockPanelSizeEvent; procedure CreateDockPanelAndSplitter; // 创建四个停靠面板和分割条 // procedure DestroyDockPanelAndSplitter;// 释放四个停靠面板和分割条 procedure CreateSplitterStyle; // 创建风格条的风格 procedure DestroySplitterStyle; // 释放风格条的风格 { 设置分割条的风格 } procedure SetSplitterStyle; // 设置风格条的风格 { 设置左边的风格条的风格 } procedure SetLeftSplitterStyle(const Value: TCnSplitterStyle); { 设置上边的风格条的风格 } procedure SetTopSplitterStyle(const Value: TCnSplitterStyle); { 设置右边的风格条的风格 } procedure SetRightSplitterStyle(const Value: TCnSplitterStyle); { 设置下边的风格条的风格 } procedure SetBottomSplitterStyle(const Value: TCnSplitterStyle); { 获得索引为Index的TCnDockPanel } function GetDockPanel(Index: Integer): TCnDockPanel; { 获得索引为Index的TCnDockSplitter } function GetDockSplitter(Index: Integer): TCnDockSplitter; procedure DoGetClientAlignControl(Align: TAlign; var Value: Integer); function GetDockPanelWithAlign(Index: TAlign): TCnDockPanel; function GetDockSplitterWithAlign(Index: TAlign): TCnDockSplitter; protected procedure GetComponentInfo(var AName, Author, Email, Comment: string); override; procedure Loaded; override; { ------------------------------------------------------------------------ } procedure DoFinishSetDockPanelSize(DockPanel: TCnDockPanel); procedure DoFloatDockClients(DockPanel: TCnDockPanel); procedure Notification(AComponent: TComponent; Operation: TOperation); override; procedure SetBottomDock(const Value: Boolean); override; procedure SetEnableDock(const Value: Boolean); override; procedure SetLeftDock(const Value: Boolean); override; procedure SetRightDock(const Value: Boolean); override; procedure SetTopDock(const Value: Boolean); override; { ------------------------------------------------------------------------ } procedure WMActivate(var Message: TWMActivate); procedure WindowProc(var Message: TMessage); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function GetClientAlignControl(Align: TAlign): Integer; property LeftDockPanel: TCnDockPanel read FLeftDockPanel; property RightDockPanel: TCnDockPanel read FRightDockPanel; property TopDockPanel: TCnDockPanel read FTopDockPanel; property BottomDockPanel: TCnDockPanel read FBottomDockPanel; property LeftSplitter: TCnDockSplitter read FLeftSplitter; property RightSplitter: TCnDockSplitter read FRightSplitter; property TopSplitter: TCnDockSplitter read FTopSplitter; property BottomSplitter: TCnDockSplitter read FBottomSplitter; property DockPanel[Index: Integer]: TCnDockPanel read GetDockPanel; property DockPanelWithAlign[Index: TAlign]: TCnDockPanel read GetDockPanelWithAlign; property DockSplitter[Index: Integer]: TCnDockSplitter read GetDockSplitter; property DockSplitterWithAlign[Index: TAlign]: TCnDockSplitter read GetDockSplitterWithAlign; property Version: string read GetDockStyleVersion; // 版本号 published property LeftSplitterStyle: TCnSplitterStyle read FLeftSplitterStyle write SetLeftSplitterStyle; property TopSplitterStyle: TCnSplitterStyle read FTopSplitterStyle write SetTopSplitterStyle; property RightSplitterStyle: TCnSplitterStyle read FRightSplitterStyle write SetRightSplitterStyle; property BottomSplitterStyle: TCnSplitterStyle read FBottomSplitterStyle write SetBottomSplitterStyle; property OnGetClientAlignSize: TGetClientAlignSizeEvent read FOnGetClientAlignSize write FOnGetClientAlignSize; property OnFinishSetDockPanelSize: TFinishSetDockPanelSizeEvent read FOnFinishSetDockPanelSize write FOnFinishSetDockPanelSize; property EnableDock; property LeftDock; property TopDock; property RightDock; property BottomDock; property DockStyle; end; { 鼠标的位置,分别是'在浮动窗体上','在平铺服务器上','在分页服务器上' } TMouseStation = (msFloat, msConjoin, msTabPage); TNCButtonEvent = procedure(DockClient: TCnDockClient; Button: TMouseButton; X, Y: Smallint; HitTest: Longint; MouseStation: TMouseStation) of object; TNCButtonDownEvent = TNCButtonEvent; TNCButtonUpEvent = TNCButtonEvent; TNCButtonDblClkEvent = TNCButtonEvent; TNCMouseMoveEvent = procedure(DockClient: TCnDockClient; X, Y: Smallint; HitTest: Longint; MouseStation: TMouseStation) of object; TPaintDockEvent = procedure(Canvas: TCanvas; Control: TControl; const ARect: TRect) of object; TPaintDockGrabberEvent = TPaintDockEvent; TPaintDockSplitterEvent = TPaintDockEvent; TFormHintEvent = procedure(HTFlag: Integer; var HintStr: string; var CanShow: Boolean) of object; { 停靠客户端 } TCnDockClient = class(TCnDockBaseControl) private FConjoinPanelClass: TCnConjoinPanelClass; FTabDockClass: TCnTabDockClass; FParentVisible: Boolean; // 父窗口是否是可见的。 FNCPopupMenu: TPopupMenu; // 当在窗体的标题栏上右击鼠标右键时,弹出的菜单。 FDirectDrag: Boolean; // 当鼠标按下的时候是否立即进行停靠操作。 FShowHint: Boolean; // 是否显示提示信息 FCanFloat: Boolean; // 是否可以浮动,这个功能是应一个用户的要求添加的, // 为了防止程序的界面过于凌乱,有时候用户并不希望 // 停靠的窗体浮动出来,就用这个属性来限制,如果是True, // 就允许浮动,否者就不允许,只能停靠到服务器上, // 当然在服务器之间是可以相互拖动的,默认为True. FRelativeServer: TCnDockServer; // 有关系的TCnDockServer的实例,这个功能也是应一个 // 用户的要求添加的,当一个客户窗体打算停靠到服务器 // 上面的时候,需要判断FRelativeServer和当前打算停靠 // 的服务器是否是同一个,如果不是,就不能停靠,否者就 // 能够停靠,默认值为nil,当这个属性是nil的时候,就说明 // 可以停靠到所有的服务器上面. FDockLevel: Integer; // 停靠的级别,只有相同级别的客户窗体才可以相互停靠 FEnableCloseBtn: Boolean; // 是否允许关闭按钮可用. FOnNCButtonDown: TNCButtonDownEvent; FOnNCButtonUp: TNCButtonUpEvent; FOnNCMouseMove: TNCMouseMoveEvent; FOnNCButtonDblClk: TNCButtonDblClkEvent; FOnPaintDockGrabber: TPaintDockGrabberEvent; FOnPaintDockSplitter: TPaintDockSplitterEvent; FOnFormShowHint: TFormHintEvent; FOnFormShow: TNotifyEvent; FOnFormHide: TNotifyEvent; FCurrentDockSite, FLastDockSite: TWinControl;// 上一次停靠的服务器 FUnDockLeft, // 浮动时候的左边的位置 FUnDockTop: Integer; // 浮动时候的右边的位置 FVSPaneWidth: Integer; // 面板的宽度 procedure SetParentVisible(const Value: Boolean); function GetLRDockWidth: Integer; function GetTBDockHeight: Integer; procedure SetLRDockWidth(const Value: Integer); procedure SetTBDockHeight(const Value: Integer); procedure SetNCPopupMenu(const Value: TPopupMenu); { ------------------------------------------------------------------------ } procedure WMNCLButtonDown(var Message: TWMNCHitMessage); procedure WMNCLButtonUp(var Message: TWMNCHitMessage); procedure WMNCLButtonDblClk(var Message: TWMNCHitMessage); procedure WMNCMButtonDown(var Message: TWMNCHitMessage); procedure WMNCMButtonUp(var Message: TWMNCHitMessage); procedure WMNCMButtonDblClk(var Message: TWMNCHitMessage); procedure WMNCRButtonDown(var Message: TWMNCHitMessage); procedure WMNCRButtonUp(var Message: TWMNCHitMessage); procedure WMNCRButtonDblClk(var Message: TWMNCHitMessage); procedure WMNCMouseMove(var Message: TWMNCHitMessage); procedure CMVisibleChanged(var Message: TMessage); procedure SetCurrentDockSite(const Value: TWinControl); procedure SetLastDockSite(const Value: TWinControl); procedure SetVSPaneWidth(const Value: Integer); procedure SetUnDockLeft(const Value: Integer); procedure SetUnDockTop(const Value: Integer); function GetDockState: Integer; procedure SetCanFloat(const Value: Boolean); procedure SetRelativeServer(const Value: TCnDockServer); procedure SetDockLevel(const Value: Integer); procedure SetEnableCloseBtn(const Value: Boolean); protected procedure GetComponentInfo(var AName, Author, Email, Comment: string); override; procedure Loaded; override; procedure DoMenuPopup(X, Y: Integer); virtual; { ------------------------------------------------------------------------ } procedure Deactivate; virtual; procedure Activate; virtual; { ------------------------------------------------------------------------ } procedure Notification(AComponent: TComponent; Operation: TOperation); override; { ------------------------------------------------------------------------ } procedure DoFloatDockClients(PanelAlign: TAlign); procedure DoFloatDockEachOther; procedure SetBottomDock(const Value: Boolean); override; procedure SetEachotherDock(const Value: Boolean); override; procedure SetEnableDock(const Value: Boolean); override; procedure SetLeftDock(const Value: Boolean); override; procedure SetRightDock(const Value: Boolean); override; procedure SetTopDock(const Value: Boolean); override; { ------------------------------------------------------------------------ } procedure DoFormOnClose(Sender: TObject; var Action: TCloseAction); override; { ------------------------------------------------------------------------ } procedure WMSize(var Message: TWMSize); procedure WMActivate(var Message: TWMActivate); // procedure WMWindowPosChanged(var Message: TWMWindowPosChanged) procedure WindowProc(var Message: TMessage); override; property RelativeServer: TCnDockServer read FRelativeServer write SetRelativeServer default nil; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; { ------------------------------------------------------------------------ } procedure FormStartDock(var Source: TCnDragDockObject); virtual; procedure FormPositionDockRect(Source: TCnDragDockObject); virtual; procedure FormDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); virtual; procedure FormDockDrop(Source: TCnDragDockObject; X, Y: Integer); virtual; procedure FormEndDock(Target: TObject; X, Y: Integer); virtual; function FormUnDock(NewTarget: TWinControl; Client: TControl): Boolean; virtual; procedure FormGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); virtual; procedure FormGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); virtual; { ------------------------------------------------------------------------ } procedure Assign(Source: TPersistent); override; { 触发OnFormShow事件 } procedure MakeShowEvent; { 触发OnFormClose事件 } procedure MakeHideEvent; { 创建DefaultConjoinPanelClass的实例 } function CreateConjoinPanelClass(ConjoinHost: TForm): TCnConjoinPanel; { 创建DefaultTabDockClass的实例 } function CreateTabDockClass(TabHost: TForm): TCnTabPageControl; { ------------------------------------------------------------------------ } function CreateConjoinHostAndDockControl(Control1, Control2: TControl; DockType: TAlign): TCnConjoinDockHostForm; virtual; function CreateTabHostAndDockControl(Control1, Control2: TControl): TCnTabDockHostForm; virtual; { ------------------------------------------------------------------------ } procedure DoNCButtonDown(Message: TWMNCHitMessage; Button: TMouseButton; MouseStation: TMouseStation); virtual; procedure DoNCButtonUp(Message: TWMNCHitMessage; Button: TMouseButton; MouseStation: TMouseStation); virtual; procedure DoNCMouseMove(Message: TWMNCHitMessage; MouseStation: TMouseStation); virtual; procedure DoNCButtonDblClk(Message: TWMNCHitMessage; Button: TMouseButton; MouseStation: TMouseStation); virtual; { ------------------------------------------------------------------------ } procedure DoPaintDockGrabber(Canvas: TCanvas; Control: TControl; const ARect: TRect); procedure DoPaintDockSplitter(Canvas: TCanvas; Control: TControl; const ARect: TRect); procedure DoFormShowHint(HTFlag: Integer; var HintStr: string; var CanShow: Boolean); { ------------------------------------------------------------------------ } procedure ShowParentForm; // 显示ParentForm procedure HideParentForm; // 隐藏ParentForm { 还原所有的停靠客户 } procedure RestoreChild; { ------------------------------------------------------------------------ } property VSPaneWidth: Integer read FVSPaneWidth write SetVSPaneWidth; { ------------------------------------------------------------------------ } { 表示父控件的可见性 } property ParentVisible: Boolean read FParentVisible write SetParentVisible; { ------------------------------------------------------------------------ } { 当前的停靠服务器 } property CurrentDockSite: TWinControl read FCurrentDockSite write SetCurrentDockSite; { 当前的停靠服务器 } property LastDockSite: TWinControl read FLastDockSite write SetLastDockSite; property UnDockLeft: Integer read FUnDockLeft write SetUnDockLeft; property UnDockTop: Integer read FUnDockTop write SetUnDockTop; { 停靠客户的状态,是在浮动状态或者停靠状态,还是其他别的状态 } property DockState: Integer read GetDockState; published property OnFormShow: TNotifyEvent read FOnFormShow write FOnFormShow; property OnFormHide: TNotifyEvent read FOnFormHide write FOnFormHide; property OnNCButtonDown: TNCButtonDownEvent read FOnNCButtonDown write FOnNCButtonDown; property OnNCButtonUp: TNCButtonUpEvent read FOnNCButtonUp write FOnNCButtonUp; property OnNCMouseMove: TNCMouseMoveEvent read FOnNCMouseMove write FOnNCMouseMove; property OnNCButtonDblClk: TNCButtonDblClkEvent read FOnNCButtonDblClk write FOnNCButtonDblClk; property OnPaintDockGrabber: TPaintDockGrabberEvent read FOnPaintDockGrabber write FOnPaintDockGrabber; property OnPaintDockSplitter: TPaintDockSplitterEvent read FOnPaintDockSplitter write FOnPaintDockSplitter; property OnFormShowHint: TFormHintEvent read FOnFormShowHint write FOnFormShowHint; { ------------------------------------------------------------------------ } property LRDockWidth: Integer read GetLRDockWidth write SetLRDockWidth; property TBDockHeight: Integer read GetTBDockHeight write SetTBDockHeight; property NCPopupMenu: TPopupMenu read FNCPopupMenu write SetNCPopupMenu; property DirectDrag: Boolean read FDirectDrag write FDirectDrag; property ShowHint: Boolean read FShowHint write FShowHint; property CanFloat: Boolean read FCanFloat write SetCanFloat default True; property DockLevel: Integer read FDockLevel write SetDockLevel default 0; property EnableCloseBtn: Boolean read FEnableCloseBtn write SetEnableCloseBtn; property EnableDock; property LeftDock; property TopDock; property RightDock; property BottomDock; property EachOtherDock; property DockStyle; end; TCnConjoinPanel = class(TCnCustomDockPanel) private FDockClient: TCnDockClient; function GetParentForm: TCnConjoinDockHostForm; // procedure SetUnDockControl(const Value: TControl); procedure CMUnDockClient(var Message: TCMUnDockClient); message CM_UNDOCKCLIENT; protected procedure ReloadDockedControl(const AControlName: string; var AControl: TControl); override; { ------------------------------------------------------------------------ } procedure CustomStartDock(var Source: TCnDragDockObject); override; procedure CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); override; procedure CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override; procedure CustomPositionDockRect(Source: TCnDragDockObject; X, Y: Integer); override; procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override; procedure CustomEndDock(Target: TObject; X, Y: Integer); override; function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override; { ------------------------------------------------------------------------ } procedure DoDockOver(Source: TDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override; procedure GetSiteInfo(Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); override; function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override; public constructor Create(AOwner: TComponent); override; procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override; property DockClient: TCnDockClient read FDockClient write FDockClient; property ParentForm: TCnConjoinDockHostForm read GetParentForm; end; TCnAdvConjoinPanel = class(TCnConjoinPanel) private procedure CMUnDockClient(var Message: TCMUnDockClient); message CM_UNDOCKCLIENT; protected procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override; function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override; { ------------------------------------------------------------------------ } function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override; public procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override; end; TCnTabPageControl = class(TCnDockPageControl) private FDockClient: TCnDockClient; FVersion: Integer; function GetParentForm: TCnTabDockHostForm; protected { ------------------------------------------------------------------------ } procedure AdjustClientRect(var Rect: TRect); override; procedure ReloadDockedControl(const AControlName: string; var AControl: TControl); override; { ------------------------------------------------------------------------ } procedure CustomStartDock(var Source: TCnDragDockObject); override; procedure CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); override; procedure CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); override; procedure CustomPositionDockRect(Source: TCnDragDockObject; X, Y: Integer); override; procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override; procedure CustomEndDock(Target: TObject; X, Y: Integer); override; function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override; procedure CustomGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); override; { ------------------------------------------------------------------------ } function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override; procedure LoadFromStream(Stream: TStream); virtual; procedure SaveToStream(Stream: TStream); virtual; property DockClient: TCnDockClient read FDockClient write FDockClient; property ParentForm: TCnTabDockHostForm read GetParentForm; property TabPosition; end; TCnAdvTabPageControl = class(TCnTabPageControl) private procedure CMUnDockClient(var Message: TCMUnDockClient); message CM_UNDOCKCLIENT; protected procedure CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); override; function CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; override; { ------------------------------------------------------------------------ } function DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; override; public destructor Destroy; override; procedure DockDrop(Source: TDragDockObject; X, Y: Integer); override; end; // TCnDockableForm是TCnConjoinDockHostForm和TCnTabDockHostForm共同的父类 // 处理一些这两个类共同的事件。 // 她上面也有一个TCnDockClient控件,也就是说她也是一个可停靠控件。 // 和一般的放了TCnDockClient控件的窗口有一样的特性。 TCnDockableForm = class(TForm) private FDockClient: TCnDockClient; FDockableControl: TWinControl; FUnDockControl: TControl; FFloatingChild: TControl; function GetDockableControl: TWinControl; procedure SetDockableControl(const Value: TWinControl); procedure SetUnDockControl(const Value: TControl); protected procedure DoClose(var Action: TCloseAction); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; property DockClient: TCnDockClient read FDockClient write FDockClient; property DockableControl: TWinControl read GetDockableControl write SetDockableControl; property UnDockControl: TControl read FUnDockControl write SetUnDockControl; property FloatingChild: TControl read FFloatingChild; published end; { 平铺的服务窗体 } TCnConjoinDockHostForm = class(TCnDockableForm) protected procedure DoClose(var Action: TCloseAction); override; public Panel: TCnConjoinPanel; constructor Create(AOwner: TComponent); override; property DockClient; published end; { 分页的服务窗体 } TCnTabDockHostForm = class(TCnDockableForm) public PageControl: TCnTabPageControl; constructor Create(AOwner: TComponent); override; destructor Destroy; override; function GetActiveDockForm: TForm; property DockClient; published end; var { 默认的TDockServer停靠Panel是TCnDockPanel } DefaultDockPanelClass: TCnDockPanelClass = TCnDockPanel; { 默认的停靠分割条是TCnDockSplitter } DefaultDockSplitterClass: TCnDockSplitterClass = TCnDockSplitter; { 默认的平铺停靠类是TCnConjoinPanel } DefaultConjoinPanelClass: TCnConjoinPanelClass = TCnConjoinPanel; { 默认的分页停靠类是TCnTabPageControl } DefaultTabDockClass: TCnTabDockClass = TCnTabPageControl; { 默认的停靠管理器节点是TCnDockZone } DefaultDockZoneClass: TCnDockZoneClass = TCnDockZone; { 默认的停靠管理器是TCnDockTree } DefaultDockTreeClass: TCnDockTreeClass = TCnDockTree; // FCnConjoinServerOptionClass: TCnBasicConjoinServerOptionClass; // FCnTabServerOptionClass: TCnBasicTabServerOptionClass; { 显示窗体 } procedure ShowDockForm(DockWindow: TWinControl); { 隐藏窗体 } procedure HideDockForm(DockWindow: TControl); { 使在Host上的TDockClient控件触发OnShowEvent或OnHideEvent,递归调用 } procedure MakeDockClientEvent(Host: TControl; Visible: Boolean); { 获得窗体的可见性 } function GetFormVisible(DockWindow: TWinControl): Boolean; { 设置分页的服务窗体中的PageControl的PopMenu } procedure SetDockPageControlPopupMenu(Value: TPopupMenu); { 设置分页的服务窗体中的PageControl的HotTrack } procedure SetDockPageControlHotTrack(Value: Boolean); { 设置分页的服务窗体的显示风格 } procedure SetTabDockHostBorderStyle(Value: TFormBorderStyle); { 设置平铺的服务窗体的显示风格 } procedure SetConjoinDockHostBorderStyle(Value: TFormBorderStyle); { 把停靠窗体的信息保存进文件 } procedure SaveDockTreeToFile(FileName: string); { 从文件里读出停靠窗体的信息 } procedure LoadDockTreeFromFile(FileName: string); { 把停靠窗体的信息保存进注册表 } procedure SaveDockTreeToReg(RootKey: DWORD; RegPath: string); { 从注册表里读出停靠窗体的信息 } procedure LoadDockTreeFromReg(RootKey: DWORD; RegPath: string); { 在窗体上查找TDockBaseControl控件 } function FindDockBaseControl(Client: TControl): TCnDockBaseControl; { 在窗体上查找TDockClient控件 } function FindDockClient(Client: TControl): TCnDockClient; { 在窗体上查找TDockServer控件 } function FindDockServer(Client: TControl): TCnDockServer; { 检查停靠服务器和客户端或者停靠客户端之间是否有停靠关系 } function IsDockable(Sender: TWinControl; Client: TControl; DropCtl: TControl = nil; DockAlign: TAlign = alNone): Boolean; { 计算停靠的区域 } function ComputeDockingRect(AControl: TControl; var DockRect: TRect; MousePos: TPoint): TAlign; { 使停靠窗体AControl从Sender中拖出来 } procedure DoFloat(Sender, AControl: TControl); { 设置控件的DockSite属性 } procedure SetDockSite(Control: TWinControl; SiteValue: Boolean); { 使DockForm控件浮动,包括它上面的DockClients } procedure DoFloatForm(DockForm: TControl); { 删除所有无用的TCnDockableForm窗体 } procedure FreeAllDockableForm; { 使应用程序的所有的窗体都浮动 } procedure DoFloatAllForm; { 得到AControl客户区中Align方向排列的子控件累加的大小到AControl边框的距离 } function GetClientAlignControlArea(AControl: TWinControl; Align: TAlign; Exclude: TControl = nil): Integer; procedure ResetDockClient(Control: TControl; NewTarget: TControl); overload; procedure ResetDockClient(DockClient: TCnDockClient; NewTarget: TControl); overload; procedure ReshowAllVisibleWindow; { 释放所有没有客户的服务窗体 } //procedure FreeAllHostOfNotClient; implementation uses Dialogs, Math, CnDockSupportProc, CnDockGlobal, CnDockInfo, CnVSNETDockStyle;//, CnVSNETDockStyle; var { 停靠的PageConrol的弹出菜单,默认为没有 } DockPageControlPopupMenu: TPopupMenu = nil; { 停靠的PageConrol是否高亮显示,默认为False } DockPageControlHotTrack: Boolean = False; { 分页式窗口的风格,默认为bsSizeToolWin } TabDockHostBorderStyle: TFormBorderStyle = bsSizeToolWin; { 平铺式窗体的风格,默认为bsSizeToolWin } ConjoinDockHostBorderStyle: TFormBorderStyle = bsSizeToolWin; { 操作系统是否是WinXP } IsWinXP: Boolean; type TCnControlAccess = class(TControl); TCnWinControlAccess = class(TWinControl); {$R CnDockableForm.DFM} {$R CnConjoinDockHost.DFM} {$R CnTabDockHost.DFM} { 在窗体上查找TDockBaseControl控件 } function FindDockBaseControl(Client: TControl): TCnDockBaseControl; var i: Integer; begin Result := nil; if Client <> nil then begin for i := 0 to Client.ComponentCount - 1 do if (Client.Components[i] is TCnDockBaseControl) then begin Result := TCnDockBaseControl(Client.Components[i]); Exit; end; end; end; { 在窗体上查找TDockServer控件 } function FindDockServer(Client: TControl): TCnDockServer; var ADockControl: TCnDockBaseControl; begin ADockControl := FindDockBaseControl(Client); if ADockControl is TCnDockServer then Result := TCnDockServer(ADockControl) else Result := nil; end; { 在窗体上查找TDockClient控件 } function FindDockClient(Client: TControl): TCnDockClient; var ADockControl: TCnDockBaseControl; begin ADockControl := FindDockBaseControl(Client); if ADockControl is TCnDockClient then Result := TCnDockClient(ADockControl) else Result := nil; end; { 检查停靠服务器和客户端或者停靠客户端之间是否有停靠关系 } function IsDockable(Sender: TWinControl; Client: TControl; DropCtl: TControl = nil; DockAlign: TAlign = alNone): Boolean; var ADockClient: TCnDockClient; i: Integer; SenderDockStyle, ClientDockStyle: TCnBasicDockStyle; SenderStyleName, ClientStyleName: string; // Level_1: Integer; Label JudgeRelation; begin ADockClient := FindDockClient(Client); Result := False; if (ADockClient <> nil) and (ADockClient.EnableDock) then begin if Sender is TCnDockPanel then begin with TCnDockPanel(Sender) do begin Result := DockServer.EnableDock and // ((DockAlign <> alClient) or (ADockClient.EachOtherDock and (DockAlign = alClient))) and (((Align = alLeft) and DockServer.LeftDock and (ADockClient.LeftDock)) or ((Align = alTop) and DockServer.TopDock and (ADockClient.TopDock)) or ((Align = alRight) and DockServer.RightDock and (ADockClient.RightDock)) or ((Align = alBottom) and DockServer.BottomDock and (ADockClient.BottomDock))); SenderDockStyle := DockServer.DockStyle; end; end else begin if (Sender <> nil) and (Sender.Parent is TCnDockableForm) then begin with TCnDockableForm(Sender.Parent).DockableControl do for i := 0 to DockClientCount - 1 do if DockClients[i] = Client then Exit; end; Result := ADockClient.EachOtherDock; if Sender <> nil then ADockClient := FindDockClient(Sender.Parent); if ADockClient <> nil then Result := Result and ADockClient.EachOtherDock; // else Result := False; if ADockClient <> nil then SenderDockStyle := ADockClient.DockStyle else Exit; end; ADockClient := FindDockClient(Client); if ADockClient <> nil then ClientDockStyle := ADockClient.DockStyle else Exit; if SenderDockStyle = nil then SenderStyleName := '' else SenderStyleName := SenderDockStyle.ClassName; if ClientDockStyle = nil then ClientStyleName := '' else ClientStyleName := ClientDockStyle.ClassName; Result := Result and (SenderStyleName = ClientStyleName); JudgeRelation: { if Result and (GlobalDockClient <> nil) then begin ADockClient := nil; Level_1 := GlobalDockClient.DockLevel; if DropCtl <> nil then ADockClient := FindDockClient(DropCtl); if ADockClient = nil then begin if Sender.Parent is TCnDockableForm then ADockClient := FindDockClient(Sender.Parent) else ADockClient := FindDockClient(Sender); end; if ADockClient <> nil then Result := Level_1 = ADockClient.DockLevel; end;} (* ADockClient := GlobalDockClient; if Result and (Sender <> nil) and (ADockClient <> nil) then begin ADockServer := FindDockServer(Sender.Parent); if (ADockServer <> nil) and (ADockClient.RelativeServer <> nil) then Result := Result and (ADockServer = ADockClient.RelativeServer) else begin ADockServer := ADockClient.RelativeServer; if Sender.Parent is TCnDockableForm then ADockClient := FindDockClient(Sender.Parent) else ADockClient := FindDockClient(Sender); if ((ADockClient <> nil) and (ADockClient.RelativeServer = nil)) or (ADockServer = nil) then Exit; if (ADockClient <> nil){ or (ADockServer <> nil) }then Result := Result and (ADockServer = ADockClient.RelativeServer); end; end;*) end; end; { 更新窗体的Caption,同时更新父窗口的Caption,递归调用 } procedure UpdateCaption(Source: TWinControl; Exclude: TControl); var i: Integer; Host: TCnDockableForm; begin if (Source <> nil) and (Source.Parent is TCnDockableForm) then begin Host := TCnDockableForm(Source.Parent); Host.Caption := ''; { 更新本身的Caption } for I := 0 to Source.DockClientCount - 1 do begin if Source.DockClients[I].Visible and (Source.DockClients[I] <> Exclude) then Host.Caption := Host.Caption + TCustomForm(Source.DockClients[I]).Caption + gs_CnStringSplitter; end; { 更新TCnTabPageControl的标签 } if (Host.HostDockSite is TCnTabPageControl) then begin with TCnTabPageControl(Host.HostDockSite) do if (ActivePage <> nil) and (ActivePage.Controls[0] = Source) then ActivePage.Caption := Host.Caption; end; UpdateCaption(Host.HostDockSite, nil); end; end; { 使停靠窗体AControl从Sender中拖出来 } procedure DoFloat(Sender, AControl: TControl); var ARect: TRect; CH{ CaptionHeight }, BW{ BorderWidth }: Integer; begin { 获得标题栏的高度和边框的宽度 } BW := Cn_GetSysBorderWidth; CH := Cn_GetSysCaptionHeight; { 进行坐标转换 } ARect.TopLeft := Sender.ClientToScreen(Point( -(BW + 3), -(CH + BW + 1))); ARect.BottomRight := Sender.ClientToScreen(Point( Sender.UndockWidth - (BW + 3), Sender.UndockHeight - (BW + CH + 1))); // Cn_LockWindow(TWinControl(Sender)); try { 使AControl浮动 } AControl.ManualFloat(ARect); finally // Cn_UnLockWindow; end; if (AControl.Left <> ARect.Left) or (AControl.Top <> ARect.Top) then begin AControl.Left := ARect.Left; AControl.Top := ARect.Top; end; // SetDockSite(AControl, True); end; { 计算停靠的区域 } function ComputeDockingRect(AControl: TControl; var DockRect: TRect; MousePos: TPoint): TAlign; var DockTopRect, DockLeftRect, DockBottomRect, DockRightRect, DockCenterRect: TRect; begin Result := alNone; // 划分停靠区域 if AControl = nil then Exit; with AControl do begin DockLeftRect.TopLeft := Point(0, 0); DockLeftRect.BottomRight := Point(ClientWidth div 5, ClientHeight); DockTopRect.TopLeft := Point(ClientWidth div 5, 0); DockTopRect.BottomRight := Point(ClientWidth div 5 * 4, ClientHeight div 5); DockRightRect.TopLeft := Point(ClientWidth div 5 * 4, 0); DockRightRect.BottomRight := Point(ClientWidth, ClientHeight); DockBottomRect.TopLeft := Point(ClientWidth div 5, ClientHeight div 5 * 4); DockBottomRect.BottomRight := Point(ClientWidth div 5 * 4, ClientHeight); DockCenterRect.TopLeft := Point(ClientWidth div 5, ClientHeight div 5); DockCenterRect.BottomRight := Point(ClientWidth div 5 * 4, ClientHeight div 5 * 4); // 发现鼠标在哪个停靠区域 if PtInRect(DockLeftRect, MousePos) then begin Result := alLeft; DockRect := DockLeftRect; DockRect.Right := ClientWidth div 2; end else if PtInRect(DockTopRect, MousePos) then begin Result := alTop; DockRect := DockTopRect; DockRect.Left := 0; DockRect.Right := ClientWidth; DockRect.Bottom := ClientHeight div 2; end else if PtInRect(DockRightRect, MousePos) then begin Result := alRight; DockRect := DockRightRect; DockRect.Left := ClientWidth div 2; end else if PtInRect(DockBottomRect, MousePos) then begin Result := alBottom; DockRect := DockBottomRect; DockRect.Left := 0; DockRect.Right := ClientWidth; DockRect.Top := ClientHeight div 2; end else if PtInRect(DockCenterRect, MousePos) then begin Result := alClient; DockRect := DockCenterRect; end; if Result = alNone then Exit; // DockRect是屏幕坐标 DockRect.TopLeft := ClientToScreen(DockRect.TopLeft); DockRect.BottomRight := ClientToScreen(DockRect.BottomRight); end; end; { 显示窗体,如果父窗口也是不可见的,就显示父窗口,递归调用 } procedure ShowDockForm(DockWindow: TWinControl); procedure ShowClient(Client, DockParent: TWinControl); var ADockClient: TCnDockClient; i: Integer; begin { 如果窗体上面还有停靠窗体,就分别调用她们的MakeDockClientEvent } if (DockParent is TCnDockableForm) and (Client <> nil) then begin with TCnDockableForm(DockParent).DockableControl do for i := 0 to DockClientCount - 1 do begin if DockClients[i] <> Client then MakeDockClientEvent(DockClients[i], True); end; if Client.HostDockSite is TCnCustomDockControl then TCnCustomDockControl(Client.HostDockSite).UpdateCaption(nil); end else begin { 否则就调用自己的MakeShowEvent } ADockClient := FindDockClient(DockParent); if ADockClient <> nil then begin ADockClient.DockStyle.ShowDockForm(ADockClient); if DockParent.CanFocus then DockParent.SetFocus; end; end; if DockParent.Parent = nil then SetForegroundWindow(DockParent.Handle); end; { 显示TCnDockPanel } function ShowDockPanel(Client: TWinControl): TWinControl; begin Result := Client; if Client <> nil then begin if Client.HostDockSite is TCnDockPanel then begin { 根据具体情况显示TCnDockPanel } TCnDockPanel(Client.HostDockSite).ShowDockPanel(True, Client, sdfDockPanel); Result := nil; end; end; end; { 显示分页窗体 } function ShowTabDockHost(Client: TWinControl): TWinControl; var i: Integer; begin Result := Client; if Client <> nil then begin ShowClient(nil, Client); if Client.HostDockSite is TCnTabPageControl then begin with TCnTabPageControl(Client.HostDockSite) do for i:=0 to PageCount - 1 do begin if Pages[i].Controls[0] = Client then begin Pages[i].Show; Break; end; end; if (Client.HostDockSite <> nil) and not (Client.HostDockSite is TCnDockPanel) then begin Result := Client.HostDockSite.Parent; ShowClient(Client, Result); if (Result <> nil) and (Result.HostDockSite is TCnTabPageControl) then Result := ShowTabDockHost(Result); end; end; end; end; { 显示平铺窗体 } function ShowConjoinDockHost(Client: TWinControl): TWinControl; begin Result := Client; if Client <> nil then begin ShowClient(nil, Client); if (Client.HostDockSite <> nil) and not (Client.HostDockSite is TCnDockPanel) then begin if Client.HostDockSite.Parent <> nil then begin Result := Client.HostDockSite.Parent; ShowClient(Client, Result); if (Result <> nil) and (Result.HostDockSite is TCnConjoinPanel) then Result := ShowConjoinDockHost(Result); end; end; end; end; procedure ShowPopupPanel(Client: TWinControl); begin if Client.HostDockSite is TCnVSPopupPanel then TCnVSPopupPanel(Client.HostDockSite).VSChannel.PopupDockForm(Client) else if (Client.HostDockSite <> nil) and (Client.HostDockSite.Parent <> nil) then begin if (Client.HostDockSite.Parent.HostDockSite is TCnVSPopupPanel) then TCnVSPopupPanel(Client.HostDockSite.Parent.HostDockSite).VSChannel.PopupDockForm(Client) else if (Client.HostDockSite.Parent.HostDockSite is TCnDockPanel) then Client.HostDockSite.Parent.HostDockSite.Invalidate; end; end; var TmpDockWindow: TWinControl; begin TmpDockWindow := DockWindow; repeat DockWindow := ShowTabDockHost(DockWindow); DockWindow := ShowConjoinDockHost(DockWindow); DockWindow := ShowDockPanel(DockWindow); until (DockWindow = nil) or (DockWindow.Parent = nil); ShowPopupPanel(TmpDockWindow); end; { 隐藏窗体,递归调用 } procedure HideDockForm(DockWindow: TControl); { 隐藏停靠客户窗体 } procedure HideDockChild(DockWindow: TControl); var i: Integer; DockClient: TCnDockClient; begin if DockWindow = nil then Exit; if (DockWindow is TCnDockableForm) and (DockWindow.Visible) then begin { 如果窗体上还有子窗体并且窗体是可见的,就调用子窗体的DockStyle的HideDockForm } with TCnDockableForm(DockWindow).DockableControl do for i := 0 to DockClientCount - 1 do HideDockChild(DockClients[i]); end; DockClient := FindDockClient(DockWindow); if (DockWindow is TForm) and (TForm(DockWindow).FormStyle <> fsMDIChild) then DockClient.DockStyle.HideDockForm(DockClient); end; { 隐藏停靠父窗体 } procedure HideDockParent(DockWindow: TControl); var Host: TWinControl; DockClient: TCnDockClient; begin if (DockWindow <> nil) and (DockWindow.HostDockSite <> nil) then begin Host := DockWindow.HostDockSite; if Host.VisibleDockClientCount = 0 then begin { 如果停靠窗体的父的上面的可见停靠窗体的个数为0,就应该隐藏这个停靠窗体的父 } if Host is TCnDockPanel then { 调整TCnDockPanel的大小 } TCnDockPanel(Host).ShowDockPanel(False, nil) else begin if Host.Parent <> nil then begin { 使停靠窗体的父隐藏,并且调用她上面的TCnDockClient的DockStyle的HideDockForm函数 } DockClient := FindDockClient(Host.Parent); DockClient.DockStyle.HideDockForm(DockClient); HideDockParent(Host.Parent); end; end end; end; end; procedure HidePopupPanel(Client: TWinControl); begin if Client.HostDockSite is TCnVSPopupPanel then TCnVSPopupPanel(Client.HostDockSite).VSChannel.HidePopupPanel(Client) else if (Client.HostDockSite <> nil) and (Client.HostDockSite.Parent <> nil) then begin if (Client.HostDockSite.Parent.HostDockSite is TCnVSPopupPanel) then TCnVSPopupPanel(Client.HostDockSite.Parent.HostDockSite).VSChannel.HidePopupPanel(Client) else if (Client.HostDockSite.Parent.HostDockSite is TCnDockPanel) then Client.HostDockSite.Parent.HostDockSite.Invalidate end; end; var TmpDockWindow: TWinControl; begin TmpDockWindow := TWinControl(DockWindow); HideDockChild(DockWindow); HideDockParent(DockWindow); if (DockWindow.HostDockSite is TCnCustomDockControl) then TCnCustomDockControl(DockWindow.HostDockSite).UpdateCaption(DockWindow); HidePopupPanel(TmpDockWindow); end; { 使在Host上的TDockClient控件触发OnShowEvent或OnHideEvent,递归调用 } procedure MakeDockClientEvent(Host: TControl; Visible: Boolean); var i: Integer; ADockClient: TCnDockClient; begin ADockClient := FindDockClient(Host); if ADockClient <> nil then begin if Visible then ADockClient.MakeShowEvent else ADockClient.MakeHideEvent; if (Host is TCnDockableForm) and (Host.Visible) then begin with TCnDockableForm(Host).DockableControl do for i := 0 to DockClientCount - 1 do MakeDockClientEvent(DockClients[i], Visible); end; end; end; { 获得窗体的可见性,递归调用 } function GetFormVisible(DockWindow: TWinControl): Boolean; var ADockClient: TCnDockClient; begin Result := True; ADockClient := FindDockClient(DockWindow); if ADockClient <> nil then begin Result := ADockClient.DockStyle.GetDockFormVisible(ADockClient); end; end; { 设置分页的服务窗体中的PageControl的PopMenu } procedure SetDockPageControlPopupMenu(Value: TPopupMenu); var i: Integer; begin DockPageControlPopupMenu := Value; for i := 0 to Screen.FormCount - 1 do begin if Screen.CustomForms[i] is TCnTabDockHostForm then TCnTabDockHostForm(Screen.CustomForms[i]).PageControl.PopupMenu := Value; end; end; { 设置分页的服务窗体中的PageControl的HotTrack } procedure SetDockPageControlHotTrack(Value: Boolean); var i: Integer; begin DockPageControlHotTrack := Value; for i := 0 to Screen.FormCount - 1 do begin if Screen.CustomForms[i] is TCnTabDockHostForm then TCnTabDockHostForm(Screen.CustomForms[i]).PageControl.HotTrack := Value; end; end; { 设置分页的服务窗体的显示风格 } procedure SetTabDockHostBorderStyle(Value: TFormBorderStyle); var i: Integer; begin TabDockHostBorderStyle := Value; for i := 0 to Screen.FormCount - 1 do begin if (Screen.CustomForms[i] is TCnTabDockHostForm) and (Screen.CustomForms[i].HostDockSite = nil) then TCnTabDockHostForm(Screen.CustomForms[i]).BorderStyle := Value; end; end; { 设置平铺的服务窗体的显示风格 } procedure SetConjoinDockHostBorderStyle(Value: TFormBorderStyle); var i: Integer; begin ConjoinDockHostBorderStyle := Value; for i := 0 to Screen.FormCount - 1 do begin if (Screen.CustomForms[i] is TCnConjoinDockHostForm) and (Screen.CustomForms[i].HostDockSite = nil) then TCnConjoinDockHostForm(Screen.CustomForms[i]).BorderStyle := Value; end; end; { 把停靠窗体的信息保存进文件 } procedure SaveDockTreeToFile(FileName: string); var CnDockInfoTree: TCnDockInfoTree; i: Integer; begin HideAllPopupPanel(nil); { 创建停靠信息树 } CnDockInfoTree := TCnDockInfoTree.Create(TCnDockInfoZone); try { 遍历应用程序的所有窗口 } for i := 0 to Screen.CustomFormCount - 1 do begin if (Screen.CustomForms[i].Parent = nil) and ((FindDockClient(Screen.CustomForms[i]) <> nil) or (FindDockServer(Screen.CustomForms[i]) <> nil)) then { 把符合条件的窗体的停靠信息保存到信息树中 } CnDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[i]); end; { 打开INI文件 } CnDockInfoTree.DockInfoIni := TIniFile.Create(FileName); try { 把信息树中的停靠信息写到INI文件中 } CnDockInfoTree.WriteInfoToIni; finally { 关闭INI文件 } CnDockInfoTree.DockInfoIni.Free; end; finally { 释放停靠信息树 } CnDockInfoTree.Free; end; end; procedure ReshowAllVisibleWindow; var i: Integer; begin if IsWinXP then begin for i := 0 to Screen.FormCount - 1 do begin if Screen.Forms[i].Visible then Windows.ShowWindow(Screen.Forms[i].Handle, SW_SHOW) else Windows.ShowWindow(Screen.Forms[i].Handle, SW_HIDE); end; end; end; { 从文件里读出停靠窗体的信息 } procedure LoadDockTreeFromFile(FileName: string); var CnDockInfoTree: TCnDockInfoTree; Form: TForm; begin HideAllPopupPanel(nil); // 创建一个没有边框,大小为0的窗体,这个窗体的作用是防止屏幕闪烁 Form := TForm.CreateNew(nil); Form.BorderStyle := bsNone; Form.BoundsRect := Rect(0,0,0,0); Form.Visible := True; Form.Name := 'A_B_C_D_E_F_G_H_I_J_K_L_M_N'; { 创建停靠信息树 } CnDockInfoTree := TCnDockInfoTree.Create(TCnDockInfoZone); { 锁住Windows桌面 } Cn_LockWindow(nil); try { 打开INI文件 } CnDockInfoTree.DockInfoIni := TIniFile.Create(FileName); try IsLoading := True; { 从INI文件里读出数据并且还原停靠信息 } CnDockInfoTree.ReadInfoFromIni; finally { 关闭INI文件 } CnDockInfoTree.DockInfoIni.Free; IsLoading := False; end; finally Form.Release; { 解锁Windows桌面 } Cn_UnLockWindow; { 释放停靠信息树 } CnDockInfoTree.Free; end; ReshowAllVisibleWindow; end; { 把停靠窗体的信息保存进注册表 } procedure SaveDockTreeToReg(RootKey: DWORD; RegPath: string); var CnDockInfoTree: TCnDockInfoTree; i: Integer; begin HideAllPopupPanel(nil); { 创建停靠信息树 } CnDockInfoTree := TCnDockInfoTree.Create(TCnDockInfoZone); try { 遍历应用程序的所有窗口 } for i := 0 to Screen.CustomFormCount - 1 do begin if (Screen.CustomForms[i].Parent = nil) and (FindDockClient(Screen.CustomForms[i]) <> nil) then { 把符合条件的窗体的停靠信息保存到信息树中(接着是停靠客户) } CnDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[i]); end; for i := 0 to Screen.CustomFormCount - 1 do begin if (Screen.CustomForms[i].Parent = nil) and (FindDockServer(Screen.CustomForms[i]) <> nil) then { 把符合条件的窗体的停靠信息保存到信息树中(首先是停靠服务器) } CnDockInfoTree.CreateZoneAndAddInfoFromApp(Screen.CustomForms[i]); end; { 打开TRegistry } CnDockInfoTree.DockInfoReg := TRegistry.Create; try { 把信息树中的停靠信息写到注册表中 } CnDockInfoTree.DockInfoReg.RootKey := RootKey; CnDockInfoTree.WriteInfoToReg(RegPath); finally { 关闭注册表 } CnDockInfoTree.DockInfoReg.Free; end; finally { 释放停靠信息树 } CnDockInfoTree.Free; end; end; { 从注册表里读出停靠窗体的信息 } procedure LoadDockTreeFromReg(RootKey: DWORD; RegPath: string); var CnDockInfoTree: TCnDockInfoTree; Form: TForm; begin HideAllPopupPanel(nil); // 创建一个没有边框,大小为0的窗体,这个窗体的作用是防止屏幕闪烁 Form := TForm.CreateNew(nil); Form.BorderStyle := bsNone; Form.BoundsRect := Rect(0,0,0,0); Form.Visible := True; Form.Name := 'A_B_C_D_E_F_G_H_I_J_K_L_M_N'; { 创建停靠信息树 } CnDockInfoTree := TCnDockInfoTree.Create(TCnDockInfoZone); { 锁住Windows桌面 } Cn_LockWindow(nil); try { 打开TRegistry } CnDockInfoTree.DockInfoReg := TRegistry.Create; try IsLoading := True; { 从注册表里读出数据并且还原停靠信息 } CnDockInfoTree.DockInfoReg.RootKey := RootKey; CnDockInfoTree.ReadInfoFromReg(RegPath); finally { 关闭TRegistry } CnDockInfoTree.DockInfoReg.Free; IsLoading := False; end; finally { 解锁Windows桌面 } Cn_UnLockWindow; { 释放停靠信息树 } CnDockInfoTree.Free; Form.Release; end; ReshowAllVisibleWindow; end; { 设置控件的DockSite属性 } procedure SetDockSite(Control: TWinControl; SiteValue: Boolean); begin TCnWinControlAccess(Control).DockSite := SiteValue; if (not (csDesigning in Control.ComponentState)) and (CnGlobalDockPresident <> nil) then CnGlobalDockPresident.RegisterDockSite(Control, SiteValue); end; { 使DockForm控件浮动,包括它上面的DockClients } procedure DoFloatForm(DockForm: TControl); var i, j: Integer; ADockServer: TCnDockServer; ARect: TRect; begin if (DockForm is TCnDockableForm) then begin {如果窗体是TCnDockableForm,就使她上面的停靠窗体浮动} with TCnDockableForm(DockForm).DockableControl do begin for i := DockClientCount - 1 downto 0 do begin DoFloatForm(DockClients[i]); end; // DockForm.Hide; DockForm.ManualDock(nil); end; end else begin ADockServer := FindDockServer(DockForm); if ADockServer <> nil then begin {如果窗体上有TCnDockServer,就使她四个上面的TCnDockPanel上面的窗体浮动} for i := 0 to 3 do begin for j := ADockServer.DockPanel[i].DockClientCount - 1 downto 0 do DoFloatForm(ADockServer.DockPanel[i].DockClients[j]); if ADockServer.DockPanel[i] is TCnVSNETDockPanel then begin with TCnVSNETDockPanel(ADockServer.DockPanel[i]).VSChannel do begin RemoveAllBlock; HidePopupPanel(ActiveDockForm); end; end; end; end else begin if DockForm.HostDockSite <> nil then begin {如果窗体是停靠状态的,就转换坐标系} if (DockForm.HostDockSite.Parent is TCnDockableForm) and (DockForm.HostDockSite.DockClientCount <= 2) then PostMessage(DockForm.HostDockSite.Parent.Handle, WM_CLOSE, 0, 0); end else ARect := DockForm.BoundsRect; // DockForm.Hide; {使窗体浮动} if DockForm.HostDockSite is TCnVSPopupPanel then begin TCnVSPopupPanel(DockForm.HostDockSite).VSNETDockPanel.VSChannel.RemoveDockControl(TWinControl(DockForm)); DockForm.Dock(nil, Bounds(DockForm.Left, DockForm.Top, DockForm.UndockWidth, DockForm.UndockHeight)); end else DockForm.ManualDock(nil); end; end; end; { 删除所有无用的TCnDockableForm窗体 } procedure FreeAllDockableForm; var i: Integer; begin Assert(CnGlobalDockPresident <> nil); for i := CnGlobalDockPresident.DockableFormList.Count - 1 downto 0 do begin if TCnDockableForm(CnGlobalDockPresident.DockableFormList[i]).DockableControl.DockClientCount = 0 then TCnDockableForm(CnGlobalDockPresident.DockableFormList[i]).Free; end; end; { 使应用程序的所有的窗体都浮动 } procedure DoFloatAllForm; var i: Integer; TempList: TList; begin TempList := TList.Create; try for i := 0 to Screen.CustomFormCount - 1 do begin // 遍历整个应用程序的所有窗体,然后把一般的窗体保存到TempList中 if not (Screen.CustomForms[i] is TCnDockableForm) then TempList.Add(Screen.CustomForms[i]); end; {遍历应用程序的窗口,使她们浮动} for i := 0 to TempList.Count - 1 do DoFloatForm(TempList[i]); finally TempList.Free; end; FreeAllDockableForm; end; { 得到AControl客户区中Align方向排列的子控件累加的大小到AControl边框的距离 } function GetClientAlignControlArea(AControl: TWinControl; Align: TAlign; Exclude: TControl): Integer; var i: Integer; begin Result := 0; for i := 0 to AControl.ControlCount - 1 do begin if (AControl.Controls[i].Align = Align) and AControl.Controls[i].Visible and (AControl.Controls[i] <> Exclude) and not ((AControl.Controls[i] is TCnDockSplitter) or (AControl.Controls[i] is TCnDockPanel)) then begin if Align in [alLeft, alRight] then Inc(Result, AControl.Controls[i].Width) else Inc(Result, AControl.Controls[i].Height); end; end; end; {******************************************************************************} { 下面的函数和方法都是内部函数 } {******************************************************************************} function GetActiveControl(AForm: TCustomForm): TWinControl; var AControl: TWinControl; begin Result := nil; AControl := AForm.ActiveControl; while AControl <> nil do begin if (AControl.HostDockSite <> nil) then begin Result := AControl; Exit; end; AControl := AControl.Parent; end; end; function GetHostDockParent(AControl: TWinControl): TWinControl; begin Result := nil; while AControl <> nil do begin if (AControl.HostDockSite <> nil) then begin Result := AControl.HostDockSite; Exit; end; AControl := AControl.Parent; end; end; { 重新设置在Control上的TCnDockClint对象的属性 } procedure ResetDockClient(Control: TControl; NewTarget: TControl); begin ResetDockClient(FindDockClient(Control), NewTarget); end; procedure ResetDockClient(DockClient: TCnDockClient; NewTarget: TControl); var point: TPoint; begin if DockClient <> nil then begin if (DockClient.ParentForm.HostDockSite is TCnDockPanel) and (NewTarget is TCnDockPanel) then begin end else begin if (DockClient.LastDockSite is TCnDockPanel) and (NewTarget is TCnDockPanel) and (DockClient.LastDockSite <> NewTarget) then begin with TCnDockPanel(DockClient.LastDockSite) do begin if UseDockManager and (CnDockManager <> nil) then CnDockManager.RemoveControl(DockClient.ParentForm); end; end; if (DockClient.ParentForm.HostDockSite is TCnDockPanel) then begin // if (NewTarget is TCnTabPageControl) or (NewTarget is TCnConjoinPanel) then // DockClient.LastDockSite := nil // else DockClient.LastDockSite := DockClient.ParentForm.HostDockSite; end else //if not (DockClient.LastDockSite is TCnDockPanel) then DockClient.LastDockSite := nil; if DockClient.ParentForm.HostDockSite = nil then begin DockClient.UnDockLeft := DockClient.ParentForm.BoundsRect.TopLeft.x; DockClient.UnDockTop := DockClient.ParentForm.BoundsRect.TopLeft.y; end else begin point := DockClient.ParentForm.BoundsRect.TopLeft; point := DockClient.ParentForm.HostDockSite.ClientToScreen(point); DockClient.UnDockLeft := point.x; DockClient.UnDockTop := point.y; end; end; end; end; { TCnDockPanel } constructor TCnDockPanel.Create(AOwner: TComponent); begin inherited Create(AOwner); DoubleBuffered := True; BevelOuter := bvNone; Width := 10; Height := 10; end; destructor TCnDockPanel.Destroy; begin inherited Destroy; end; procedure TCnDockPanel.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); begin inherited CustomDockDrop(Source, X, Y); // 如果Source.Control是TCnDockableForm,就有可能被释放掉,如果它已经被释放掉了, // 就不要再调用ShowDockPanel函数了,不然会使TCnDockPanel宽度或者高度为0。 if Source.Control <> nil then ShowDockPanel(True, Source.Control); end; procedure TCnDockPanel.CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign); if Accept then inherited CustomDockOver(Source, X, Y, State, Accept); end; procedure TCnDockPanel.CustomEndDock(Target: TObject; X, Y: Integer); begin inherited CustomEndDock(Target, X, Y); end; procedure TCnDockPanel.CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); begin inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock); CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign); end; procedure TCnDockPanel.CustomPositionDockRect(Source: TCnDragDockObject; X, Y: Integer); var ARect: TRect; // Tmp: Integer; { function GetParentClientW(Control: TWinControl): Integer; var i: Integer; begin Result := Control.ClientWidth; for i := 0 to Control.ControlCount - 1 do begin if (Control.Controls[i].Align in [alLeft, alRight]) and (Control.Controls[i] <> Self) and (Control.Controls[i].Visible) then Result := Result - Control.Controls[i].Width; end; end; function GetParentClientH(Control: TWinControl): Integer; var i: Integer; begin Result := Control.ClientHeight; for i := 0 to Control.ControlCount - 1 do begin if (Control.Controls[i].Align in [alTop, alBottom]) and (Control.Controls[i] <> Self) and (Control.Controls[i].Visible) then Result := Result - Control.Controls[i].Height; end; end; var ParentClientW, ParentClientH: Integer;} begin inherited CustomPositionDockRect(Source, X, Y); if VisibleDockClientCount = 0 then begin // 记住DockRect预览停靠区域. if GlobalDockClient <> nil then begin case Align of alTop: begin ARect.TopLeft := ClientToScreen(Point(0, 0)); ARect.BottomRight := ClientToScreen( Point(Width, Source.Control.TBDockHeight)); end; alBottom: begin ARect.TopLeft := ClientToScreen( Point(0, -Source.Control.TBDockHeight)); ARect.BottomRight := ClientToScreen( Point(Width, 0)); end; alLeft: begin ARect.TopLeft := ClientToScreen(Point(0, 0)); ARect.BottomRight := ClientToScreen( Point(Source.Control.LRDockWidth, Height)); end; alRight: begin ARect.TopLeft := ClientToScreen(Point(-Source.Control.LRDockWidth, 0)); ARect.BottomRight := ClientToScreen(Point(Width, Height)); end; end; Source.DockRect := ARect; end; end; end; procedure TCnDockPanel.CustomStartDock(var Source: TCnDragDockObject); begin inherited CustomStartDock(Source); end; function TCnDockPanel.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; begin ShowDockPanel(False, nil); Result := inherited CustomUnDock(Source, NewTarget, Client); if not (Client is TCnDockableForm) then SetDockSite(TForm(Client), True); end; procedure TCnDockPanel.ReloadDockedControl(const AControlName: string; var AControl: TControl); begin { 在整个应用程序中查找名字为AControlName的窗体 } AControl := Cn_FindDockFormWithName(AControlName); end; procedure TCnDockPanel.ShowDockPanel(MakeVisible: Boolean; Client: TControl; PanelSizeFrom: TSetDockPanelSizeFrom); const DefaultDockSize = 100; // 默认的宽度或者大小 var DockHeight, DockWidth: Integer; //Label Loop; begin if (not MakeVisible and (VisibleDockClientCount > 1)) or (GlobalDockClient = nil){ or IsLoading }then Exit; { 设置TCnDockSplitter的属性 } DockServer.DockSplitter[Integer(Align) - 1].Visible := MakeVisible; if (MakeVisible and (Client <> nil)) then// or ((not MakeVisible) and (Client <> nil)) then begin if (Width * Height = 0) then begin { 如果宽度和高度有一个是0 } if (PanelSizeFrom = sdfDockPanel) or (Client = nil) then begin DockHeight := TBDockHeight; DockWidth := LRDockWidth; end else begin DockHeight := Client.TBDockHeight; DockWidth := Client.LRDockWidth; end; { 不能等于0,不然显示会不正常 } if DockHeight = 0 then DockHeight := DefaultDockSize; if DockWidth = 0 then DockWidth := DefaultDockSize; Parent.DisableAlign; try case Align of alTop: begin { 如果主窗体的客户区的上边有别的控件,就把这个高度计算在内 } Top := DockServer.GetClientAlignControl(alTop); Height := DockHeight; DockServer.TopSplitter.Top := Top + Height; end; alBottom: begin { 如果主窗体的客户区的下边有别的控件,就把这个高度计算在内 } Top := Parent.ClientHeight - DockServer.GetClientAlignControl(alBottom) - DockHeight + 1; Height := DockHeight; DockServer.BottomSplitter.Top := Top + DockServer.BottomSplitter.Height; end; alLeft: begin { 如果主窗体的客户区的左边有别的控件,就把这个宽度计算在内 } Left := DockServer.GetClientAlignControl(alLeft); Width := DockWidth; DockServer.LeftSplitter.Left := Left + Width; end; alRight: begin { 如果主窗体的客户区的右边有别的控件,就把这个宽度计算在内 } Width := DockWidth; Left := Parent.ClientWidth - DockServer.GetClientAlignControl(alRight) - DockWidth + 1; DockServer.RightSplitter.Left := Left - DockServer.RightSplitter.Width; end; end; finally Parent.EnableAlign; if UseDockManager and (CnDockManager <> nil) then CnDockManager.ResetBounds(True); end; DockServer.DoFinishSetDockPanelSize(Self); end; end else begin // if (Client <> nil) and (not Client.Visible) then goto Loop; if (PanelSizeFrom = sdfDockPanel) or (Client = nil) then begin if Height > 0 then TBDockHeight := Height; if Width > 0 then LRDockWidth := Width; end else begin if Height > 0 then Client.TBDockHeight := Height; if Width > 0 then Client.LRDockWidth := Width; end; if Align in [alLeft, alRight] then Width := 0 else Height := 0; { 重新设置位置 } ResetPosition; end; { 条件满足的话就显示Client } //Loop: if MakeVisible and (Client <> nil) then begin if (not Client.Visible) then Client.Show; if (not TWinControl(Client).Focused) and (TWinControl(Client).CanFocus) then TWinControl(Client).SetFocus; end; end; function TCnDockPanel.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin Result := IsDockable(Self, Client); ShowDockPanel(False, nil); Result := Result and (Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0); if Result then begin if not (Client is TCnDockableForm) then SetDockSite(TForm(Client), True); end; end; procedure TCnDockPanel.DockDrop(Source: TDragDockObject; X, Y: Integer); begin if Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0 then begin if Source.Control is TForm then begin TForm(Source.Control).ActiveControl := nil; { ------------------------------------------------------------------------ } SetDockSite(TForm(Source.Control), False); end; UpdateCaption(nil); end; ShowDockPanel(TWinControl(Source.DragTarget).VisibleDockClientCount > 0, Source.Control); end; function TCnDockPanel.GetPanelIndex: Integer; begin case Align of alTop: Result := 0; alBottom: Result := 1; alLeft: Result := 2; alRight: Result := 3; else Result := -1; end; end; procedure TCnDockPanel.Resize; begin inherited; end; procedure TCnDockPanel.SetDockServer(const Value: TCnDockServer); begin FDockServer := Value; end; procedure TCnDockPanel.ResetPosition; begin { 调整TCnDockPanel的位置,使它总是在服务窗体的客户区的最内层 } case Align of alLeft: Left := GetClientAlignControlArea(Parent, Align) + 1; alRight: Left := Parent.ClientWidth - GetClientAlignControlArea(Parent, Align) - Width - 1; alTop: Top := GetClientAlignControlArea(Parent, Align) + 1; alBottom: Top := Parent.ClientHeight - GetClientAlignControlArea(Parent, Align) - Height - 1; end; end; { TCnAdvDockPanel } //procedure TCnAdvDockPanel.CMDockClient(var Message: TCMDockClient); //var DockClient: TCnDockClient; //begin { with Message do begin DockClient := FindDockClient(DockSource.Control); if DockClient <> nil then begin if (DockClient.LastDockSite is TCnDockPanel) then begin with TCnDockPanel(DockClient.LastDockSite) do begin if UseDockManager and (CnDockManager <> nil) then CnDockManager.RemoveControl(DockSource.Control); end; end else begin end; end; end;} //end; procedure TCnAdvDockPanel.CMUnDockClient(var Message: TCMUnDockClient); var DockClient: TCnDockClient; begin if IsLoading then Exit; with Message do begin Result := 0; if UseDockManager and (CnDockManager <> nil) then begin DockClient := FindDockClient(Client); if (NewTarget <> nil) or ((Client <> nil) and (csDestroying in Client.ComponentState)) then begin if DockClient <> nil then DockClient.LastDockSite := nil; CnDockManager.RemoveControl(Client); end else begin if (DockClient <> nil) then DockClient.LastDockSite := Self; CnDockManager.HideControl(Client); end; end; end; end; procedure TCnAdvDockPanel.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, Source.TargetControl); inherited; end; function TCnAdvDockPanel.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, NewTarget); Result := inherited CustomUnDock(Source, NewTarget, Client) end; procedure TCnAdvDockPanel.DockDrop(Source: TDragDockObject; X, Y: Integer); begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, TControl(Source.DragTarget)); inherited; end; function TCnAdvDockPanel.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Client, NewTarget); Result := inherited DoUnDock(NewTarget, Client); end; { TCnSplitterStyle } procedure TCnSplitterStyle.Assign(Source: TPersistent); begin if Source is TCnSplitterStyle then begin Color := TCnSplitterStyle(Source).Color; Cursor := TCnSplitterStyle(Source).Cursor; ParentColor := TCnSplitterStyle(Source).ParentColor; ResizeStyle := TCnSplitterStyle(Source).ResizeStyle; Size := TCnSplitterStyle(Source).Size; end; inherited Assign(Source); end; procedure TCnSplitterStyle.AssignTo(Dest: TPersistent); begin if Dest is TCnSplitterStyle then with TCnSplitterStyle(Dest) do begin Color := Self.Color; Cursor := Self.Cursor; ParentColor := Self.ParentColor; ResizeStyle := Self.ResizeStyle; Size := Self.Size; end else inherited AssignTo(Dest); end; procedure TCnSplitterStyle.AssignToSplitter(Dest: TCnDockSplitter); begin Dest.Color := Color; Dest.Cursor := Cursor; Dest.ParentColor := ParentColor; Dest.ResizeStyle := ResizeStyle; if Dest.Align in [alTop, alBottom] then Dest.Height := Size else Dest.Width := Size; end; constructor TCnSplitterStyle.Create(ASplitter: TCnDockSplitter; ACursor: TCursor); begin inherited Create; FSplitter := ASplitter; Color := clBtnFace; Cursor := ACursor; ParentColor := False; ResizeStyle := rsPattern; FSize := 3; FMinSize := 30; end; procedure TCnSplitterStyle.SetColor(const Value: TColor); begin if FColor <> Value then begin FColor := Value; ParentColor := False; if Assigned(FSplitter) then FSplitter.Color := Value; end; end; procedure TCnSplitterStyle.SetCursor(const Value: TCursor); begin FCursor := Value; if Assigned(FSplitter) then FSplitter.Cursor := Value; end; procedure TCnSplitterStyle.SetMinSize(const Value: TCnSplitterSize); begin FMinSize := Value; if Assigned(FSplitter) then FSplitter.MinSize := Value; end; procedure TCnSplitterStyle.SetParentColor(const Value: Boolean); begin if FParentColor <> Value then begin FParentColor := Value; if Value then FColor := FDockServer.ParentForm.Color; if Assigned(FSplitter) then FSplitter.ParentColor := Value; end; end; procedure TCnSplitterStyle.SetResizeStyle(const Value: TResizeStyle); begin FResizeStyle := Value; if Assigned(FSplitter) then FSplitter.ResizeStyle := Value; end; procedure TCnSplitterStyle.SetSize(const Value: TCnSplitterSize); begin FSize := Value; if Assigned(FSplitter) then begin if FSplitter.Align in [alTop, alBottom] then FSplitter.Height := Value else FSplitter.Width := Value; end; end; procedure TCnSplitterStyle.SetSplitterStyle; begin AssignToSplitter(FSplitter); end; { TCnDockBaseControl } destructor TCnDockBaseControl.Destroy; begin if not (csDesigning in ComponentState) then begin if @FOldWindowProc <> nil then FParentForm.WindowProc := FOldWindowProc; FOldWindowProc := nil; if Assigned(FDockStyle) and not (FDockStyle is TCnBasicDockStyle) then FDockStyle.SetDockBaseControl(False, Self); end; if FDockStyle <> nil then FDockStyle.RemoveDockBaseControl(Self); inherited Destroy; end; constructor TCnDockBaseControl.Create(AOwner: TComponent); begin inherited Create(AOwner); FParentForm := TForm(AOwner); FEnableDock := True; FLeftDock := True; FTopDock := True; FRightDock := True; FBottomDock := True; FEachOtherDock := True; FDockStyle := nil; if not (csDesigning in ComponentState) then begin FOldOnClose := FParentForm.OnClose; ParentForm.OnClose := DoFormOnClose; FOldOnCreate := FParentForm.OnCreate; ParentForm.OnCreate := DoFormOnCreate; { 保存老的窗口过程 } FOldWindowProc := FParentForm.WindowProc; { 重载窗口过程 } FParentForm.WindowProc := WindowProc; end; end; procedure TCnDockBaseControl.SetBottomDock(const Value: Boolean); begin if CanSetBottomDocked then FBottomDock := Value; end; procedure TCnDockBaseControl.SetEachotherDock(const Value: Boolean); begin if CanSetEachOtherDocked then FEachotherDock := Value; end; procedure TCnDockBaseControl.SetEnableDock(const Value: Boolean); begin if CanSetEnableDocked then FEnableDock := Value; end; procedure TCnDockBaseControl.SetLeftDock(const Value: Boolean); begin if CanSetLeftDocked then FLeftDock := Value; end; procedure TCnDockBaseControl.SetRightDock(const Value: Boolean); begin if CanSetRightDocked then FRightDock := Value; end; procedure TCnDockBaseControl.SetTopDock(const Value: Boolean); begin if CanSetTopDocked then FTopDock := Value; end; procedure TCnDockBaseControl.Assign(Source: TPersistent); begin if Source is TCnDockBaseControl then begin FEnableDock := TCnDockBaseControl(Source).EnableDock; FLeftDock := TCnDockBaseControl(Source).LeftDock; FTopDock := TCnDockBaseControl(Source).TopDock; FRightDock := TCnDockBaseControl(Source).RightDock; FBottomDock := TCnDockBaseControl(Source).BottomDock; FEachOtherDock := TCnDockBaseControl(Source).EachOtherDock; FDockStyle := TCnDockBaseControl(Source).DockStyle; if FDockStyle <> nil then FDockStyle.AddDockBaseControl(Self); end else inherited Assign(Source); end; procedure TCnDockBaseControl.SetDockStyle(const Value: TCnBasicDockStyle); begin { 根据原来的FDockStyle和输入的Value添加TCnDockBaseControl对象 } if FDockStyle <> nil then FDockStyle.RemoveDockBaseControl(Self); if Value <> nil then Value.AddDockBaseControl(Self); if (csDesigning in ComponentState) or (csLoading in ComponentState) then begin // 当前是设计期,就修改FDockStyle的值。 if Value <> nil then begin Value.SetDockBaseControl(True, Self); // 调用FreeNotification函数来使一个窗体的TCnDockBaseControl设置DockStyle // 属性,这个DockStyle属性是另一个窗体上的控件,不会出现异常。 Value.FreeNotification(ParentForm); end; FDockStyle := Value; end else begin // 在运行期不能设置DockStyle属性。 ShowMessage(gs_CannotChangeDockStyleProperty); end; end; procedure TCnDockBaseControl.Notification(AComponent: TComponent; Operation: TOperation); begin // 当DockStyle属性的控件插入或者释放的时候,会调用Notification函数。 inherited Notification(AComponent, Operation); if Operation = opRemove then // 如果是释放,就要把DockStyle属性设成nil。 if AComponent = FDockStyle then begin FDockStyle.SetDockBaseControl(False, Self); FDockStyle := nil; end; end; procedure TCnDockBaseControl.Loaded; begin if not (csDesigning in ComponentState) then begin // 默认的DockStyle属性是TCnBasicDockStyle类。 if not Assigned(DockStyle) then DockStyle := TCnBasicDockStyle.Create(ParentForm); if Assigned(DockStyle) then begin DockStyle.SetDockBaseControl(True, Self); end; end; inherited Loaded; end; function TCnDockBaseControl.GetDockStyleVersion: string; begin Result := gs_CnDockManagerVersion; end; procedure TCnDockBaseControl.DoFormOnClose(Sender: TObject; var Action: TCloseAction); begin if (Action = caFree) and (CnGlobalDockPresident <> nil) then begin { 把ParentForm添加到CnDockManager中 } if Self is TCnDockServer then CnGlobalDockPresident.RemoveDockServerFromDockManager(ParentForm) else if Self is TCnDockClient then CnGlobalDockPresident.RemoveDockClientFromDockManager(ParentForm); end; if Assigned(FOldOnClose) then FOldOnClose(Sender, Action); end; procedure TCnDockBaseControl.DoFormOnCreate(Sender: TObject); begin if CnGlobalDockPresident <> nil then begin { 从CnDockManager清除ParentForm } if Self is TCnDockServer then CnGlobalDockPresident.AddDockServerToDockManager(ParentForm) else if Self is TCnDockClient then CnGlobalDockPresident.AddDockClientToDockManager(ParentForm); end; if Assigned(FOldOnCreate) then FOldOnCreate(Sender); end; procedure TCnDockBaseControl.WindowProc(var Message: TMessage); begin if not (csDesigning in ComponentState) then begin if Assigned(FOldWindowProc) then FOldWindowProc(Message); end; end; procedure TCnDockBaseControl.SetParentComponent(Value: TComponent); var ADockBaseControl: TCnDockBaseControl; begin ADockBaseControl := FindDockBaseControl(ParentForm); if (Assigned(ADockBaseControl)) and (ADockBaseControl <> Self) then raise EInvalidOperation.Create(Format(gs_CannotLayAnother, [ADockBaseControl.ClassName, ClassName])); inherited SetParentComponent(Value); end; function TCnDockBaseControl.CanSetBottomDocked: Boolean; begin Result := True; if DockStyle <> nil then Result := DockStyle.CanSetBottomDocked(Self); end; function TCnDockBaseControl.CanSetEachOtherDocked: Boolean; begin Result := True; if DockStyle <> nil then Result := DockStyle.CanSetEachOtherDocked(Self); end; function TCnDockBaseControl.CanSetEnableDocked: Boolean; begin Result := True; if DockStyle <> nil then Result := DockStyle.CanSetEnableDocked(Self); end; function TCnDockBaseControl.CanSetLeftDocked: Boolean; begin Result := True; if DockStyle <> nil then Result := DockStyle.CanSetLeftDocked(Self); end; function TCnDockBaseControl.CanSetRightDocked: Boolean; begin Result := True; if DockStyle <> nil then Result := DockStyle.CanSetRightDocked(Self); end; function TCnDockBaseControl.CanSetTopDocked: Boolean; begin Result := True; if DockStyle <> nil then Result := DockStyle.CanSetTopDocked(Self); end; { TCnDockServer } constructor TCnDockServer.Create(AOwner: TComponent); begin inherited Create(AOwner); CreateSplitterStyle; end; destructor TCnDockServer.Destroy; begin // if not (csDesigning in ComponentState) then // DestroyDockPanelAndSplitter; DestroySplitterStyle; inherited Destroy; end; procedure TCnDockServer.CreateDockPanelAndSplitter; var ControlList: TList; function CreatePanel(Align: TAlign; Name: string): TCnDockPanel; begin if (FDockPanelClass <> nil) and (FDockPanelClass <> TCnDockPanelClass(ClassType)) then begin Result := FDockPanelClass.Create(Owner); Result.Parent := ParentForm; Result.Name := Name; Result.Caption := ''; Result.Align := Align; Result.DockServer := Self; Result.ResetPosition; if Align in [alTop, alBottom] then Result.Height := 0 else Result.Width := 0; SetDockSite(Result, True); { 调用DockStyle的AssignConjoinServerOption函数重新设置平铺属性 } if DockStyle <> nil then DockStyle.AssignConjoinServerOption(Result); end else Result := nil; end; function CreateSplitter(Align: TAlign; Name: string): TCnDockSplitter; begin if (FDockSplitterClass <> nil) and (FDockSplitterClass <> TCnDockSplitterClass(ClassType)) then begin Result := FDockSplitterClass.Create(Owner); Result.Parent := ParentForm; Result.Name := Name; Result.Visible := False; Result.Align := Align; Result.DockServer := Self; end else Result := nil; end; begin ControlList := TList.Create; try FLeftDockPanel := CreatePanel(alLeft, 'LeftDockPanel_A_B_C_D_E_F_G'); FLeftSplitter := CreateSplitter(alLeft, 'LeftSplitter_A_B_C_D_E_F_G'); FRightDockPanel := CreatePanel(alRight, 'RightDockPanel_A_B_C_D_E_F_G'); FRightSplitter := CreateSplitter(alRight, 'RightSplitter_A_B_C_D_E_F_G'); FTopDockPanel := CreatePanel(alTop, 'TopDockPanel_A_B_C_D_E_F_G'); FTopSplitter := CreateSplitter(alTop, 'TopSplitter_A_B_C_D_E_F_G'); FBottomDockPanel := CreatePanel(alBottom, 'BottomDockPanel_A_B_C_D_E_F_G'); FBottomSplitter := CreateSplitter(alBottom, 'BottomSplitter_A_B_C_D_E_F_G'); finally ControlList.Free; end; end; procedure TCnDockServer.CreateSplitterStyle; begin FLeftSplitterStyle := TCnSplitterStyle.Create(FLeftSplitter, crHSplit); FTopSplitterStyle := TCnSplitterStyle.Create(FTopSplitter, crVSplit); FRightSplitterStyle := TCnSplitterStyle.Create(FRightSplitter, crHSplit); FBottomSplitterStyle := TCnSplitterStyle.Create(FBottomSplitter, crVSplit); FLeftSplitterStyle.FDockServer := Self; FTopSplitterStyle.FDockServer := Self; FRightSplitterStyle.FDockServer := Self; FBottomSplitterStyle.FDockServer := Self; end; procedure TCnDockServer.DestroySplitterStyle; begin FLeftSplitterStyle.Free; FTopSplitterStyle.Free; FRightSplitterStyle.Free; FBottomSplitterStyle.Free; end; procedure TCnDockServer.SetLeftSplitterStyle( const Value: TCnSplitterStyle); begin FLeftSplitterStyle.Assign(Value); end; procedure TCnDockServer.SetTopSplitterStyle( const Value: TCnSplitterStyle); begin FTopSplitterStyle.Assign(Value); end; procedure TCnDockServer.SetRightSplitterStyle( const Value: TCnSplitterStyle); begin FRightSplitterStyle.Assign(Value); end; procedure TCnDockServer.SetBottomSplitterStyle( const Value: TCnSplitterStyle); begin FBottomSplitterStyle.Assign(Value); end; procedure TCnDockServer.SetSplitterStyle; begin LeftSplitterStyle.Splitter := FLeftSplitter; LeftSplitterStyle.SetSplitterStyle; TopSplitterStyle.Splitter := FTopSplitter; TopSplitterStyle.SetSplitterStyle; RightSplitterStyle.Splitter := FRightSplitter; RightSplitterStyle.SetSplitterStyle; BottomSplitterStyle.Splitter := FBottomSplitter; BottomSplitterStyle.SetSplitterStyle; end; procedure TCnDockServer.WindowProc(var Message: TMessage); begin { 执行FDockStyle的消息处理 } if Assigned(FDockStyle) then begin if FDockStyle.DockServerWindowProc(Self, Message) then { 如果DockStyle已经执行了消息处理,就不执行默认的处理 } Exit; end; if not (csDesigning in ComponentState) then begin if Message.Msg = WM_ACTIVATE then WMActivate(TWMActivate(Message)); end; inherited WindowProc(Message); end; function TCnDockServer.GetDockPanel(Index: Integer): TCnDockPanel; begin Result := nil; case Index of 0: Result := FTopDockPanel; 1: Result := FBottomDockPanel; 2: Result := FLeftDockPanel; 3: Result := FRightDockPanel; end; end; function TCnDockServer.GetDockSplitter(Index: Integer): TCnDockSplitter; begin Result := nil; case Index of 0: Result := FTopSplitter; 1: Result := FBottomSplitter; 2: Result := FLeftSplitter; 3: Result := FRightSplitter; end; end; procedure TCnDockServer.SetBottomDock(const Value: Boolean); begin if not Value then DoFloatDockClients(BottomDockPanel); inherited SetBottomDock(Value); end; procedure TCnDockServer.SetEnableDock(const Value: Boolean); begin if not Value then begin DoFloatDockClients(TopDockPanel); DoFloatDockClients(BottomDockPanel); DoFloatDockClients(LeftDockPanel); DoFloatDockClients(RightDockPanel); end; inherited SetEnableDock(Value); end; procedure TCnDockServer.SetLeftDock(const Value: Boolean); begin if not Value then DoFloatDockClients(LeftDockPanel); inherited SetLeftDock(Value); end; procedure TCnDockServer.SetRightDock(const Value: Boolean); begin if not Value then DoFloatDockClients(RightDockPanel); inherited SetRightDock(Value); end; procedure TCnDockServer.SetTopDock(const Value: Boolean); begin if not Value then DoFloatDockClients(TopDockPanel); inherited SetTopDock(Value); end; procedure TCnDockServer.DoFloatDockClients(DockPanel: TCnDockPanel); var i: Integer; ADockClient: TCnDockClient; begin if not (csDesigning in ComponentState) and (DockPanel <> nil) then begin for i := DockPanel.DockClientCount - 1 downto 0 do begin ADockClient := FindDockClient(DockPanel.DockClients[i]); if ADockClient <> nil then ADockClient.RestoreChild; end; end; end; procedure TCnDockServer.GetComponentInfo(var AName, Author, Email, Comment: string); begin AName := SCnDockServerName; Author := SCnPack_LuXiaoban; Email := SCnPack_LuXiaobanEmail; Comment := SCnDockServerComment; end; procedure TCnDockServer.Loaded; begin if Assigned(DockStyle) and Assigned(DockStyle.CnDockPanelClass) then FDockPanelClass := DockStyle.CnDockPanelClass else FDockPanelClass := DefaultDockPanelClass; { ------------------------------------------------------------------------ } if Assigned(DockStyle) and Assigned(DockStyle.CnDockSplitterClass) then FDockSplitterClass := DockStyle.CnDockSplitterClass else FDockSplitterClass := DefaultDockSplitterClass; { ------------------------------------------------------------------------ } if not (csDesigning in ComponentState) then begin CreateDockPanelAndSplitter; SetSplitterStyle; end; inherited Loaded; end; procedure TCnDockServer.WMActivate(var Message: TWMActivate); var i: Integer; Control: TWinControl; begin if (Message.Active = WA_INACTIVE) then begin for i := 0 to 3 do DockPanel[i].CnDockManager.ActiveControl := nil; end else begin Control := GetActiveControl(ParentForm); for i := 0 to 3 do begin if GetHostDockParent(Control) = DockPanel[i] then begin DockPanel[i].CnDockManager.ActiveControl := Control; if Control.CanFocus then Control.SetFocus; end; end; end; end; procedure TCnDockServer.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); end; procedure TCnDockServer.DoGetClientAlignControl(Align: TAlign; var Value: Integer); begin if Assigned(FOnGetClientAlignSize) then FOnGetClientAlignSize(Align, Value); end; procedure TCnDockServer.DoFinishSetDockPanelSize(DockPanel: TCnDockPanel); begin if Assigned(FOnFinishSetDockPanelSize) then FOnFinishSetDockPanelSize(DockPanel); end; function TCnDockServer.GetClientAlignControl(Align: TAlign): Integer; begin Result := GetClientAlignControlArea(ParentForm, Align); DoGetClientAlignControl(Align, Result); end; function TCnDockServer.GetDockPanelWithAlign(Index: TAlign): TCnDockPanel; begin Result := nil; case Index of alLeft: Result := FLeftDockPanel; alRight: Result := FRightDockPanel; alTop: Result := FTopDockPanel; alBottom: Result := FBottomDockPanel; end; end; function TCnDockServer.GetDockSplitterWithAlign( Index: TAlign): TCnDockSplitter; begin Result := nil; case Index of alLeft: Result := FLeftSplitter; alRight: Result := FRightSplitter; alTop: Result := FTopSplitter; alBottom: Result := FBottomSplitter; end; end; { TCnDockClient } constructor TCnDockClient.Create(AOwner: TComponent); begin inherited Create(AOwner); FParentVisible := ParentForm.Visible; ParentForm.DragKind := dkDock; ParentForm.DragMode := dmAutomatic; ParentForm.UseDockManager := False; if not (ParentForm is TCnDockableForm) then SetDockSite(ParentForm, True); LRDockWidth := 100; TBDockHeight := 100; if GlobalDockClient = nil then GlobalDockClient := Self; FDirectDrag := False; FShowHint := True; FCanFloat := True; FRelativeServer := nil; FDockLevel := 0; EnableCloseBtn := True; end; destructor TCnDockClient.Destroy; begin if not (ParentForm is TCnDockableForm) then SetDockSite(ParentForm, False); ParentForm.DragKind := dkDrag; ParentForm.DragMode := dmManual; inherited Destroy; end; procedure TCnDockClient.Assign(Source: TPersistent); begin if Source is TCnDockClient then begin FConjoinPanelClass := TCnDockClient(Source).FConjoinPanelClass; FTabDockClass := TCnDockClient(Source).FTabDockClass; FParentVisible := TCnDockClient(Source).FParentVisible; FNCPopupMenu := TCnDockClient(Source).FNCPopupMenu; FDirectDrag := TCnDockClient(Source).FDirectDrag; FShowHint := TCnDockClient(Source).FShowHint; FCanFloat := TCnDockClient(Source).FCanFloat; FRelativeServer := TCnDockClient(Source).FRelativeServer; FDockLevel := TCnDockClient(Source).DockLevel; end; inherited Assign(Source); end; procedure TCnDockClient.WindowProc(var Message: TMessage); var OldOrient: TDockOrientation; begin { 执行FDockStyle的消息处理 } if Assigned(FDockStyle) then begin if FDockStyle.DockClientWindowProc(Self, Message) then { 如果DockStyle已经执行了消息处理,就不执行默认的处理 } Exit; end; if not (csDesigning in ComponentState) then begin case Message.Msg of CM_SHOWINGCHANGED: begin { 在WinXP中,当在装载的时候,会出现闪烁,在这里把显示改变的消息捕获掉。 在98或者2000中是没有这种情况的,因为已经调用了LockWindowUpdate函数, 但是在XP下面不知道是什么原因,还是会出现这种闪烁的情况,没办法,只能 在装载的时候先过滤掉CM_SHOWINGCHANGED消息,然后在装载的最后把应该显示 但是没有显示的客户窗体显示出来。} if IsWinXP and IsLoading then Exit; end; WM_NCLBUTTONDOWN: begin // 如果捕获到WM_NCLBUTTONDOWN消息,就调用WMNCLButtonDown函数。 WMNCLButtonDown(TWMNCHitMessage(Message)); if (Message.Result = 1) then Exit; end; WM_NCLBUTTONUP: WMNCLButtonUp(TWMNCHitMessage(Message)); WM_NCLBUTTONDBLCLK: WMNCLButtonDblClk(TWMNCHitMessage(Message)); WM_NCMBUTTONDOWN: WMNCMButtonDown(TWMNCHitMessage(Message)); WM_NCMBUTTONUP: WMNCMButtonUp(TWMNCHitMessage(Message)); WM_NCMBUTTONDBLCLK: WMNCMButtonDblClk(TWMNCHitMessage(Message)); WM_NCRBUTTONDOWN: begin WMNCRButtonDown(TWMNCHitMessage(Message)); { 如果有自定义的弹出菜单,就不要调用系统默认的弹出菜单, 要实现这个功能,只要不把这个消息发送给Windows就可以办到。 } if FNCPopupMenu <> nil then Exit; end; WM_NCRBUTTONUP: WMNCRButtonUp(TWMNCHitMessage(Message)); WM_NCRBUTTONDBLCLK: WMNCRButtonDblClk(TWMNCHitMessage(Message)); WM_NCMOUSEMOVE: WMNCMOUSEMOVE(TWMNCHitMessage(Message)); WM_SIZE: WMSize(TWMSize(Message)); WM_ACTIVATE: WMActivate(TWMActivate(Message)); WM_WINDOWPOSCHANGED: begin //下面一条代码的作用是使TControl.CalcDockSizes方法失效, //请参看Controls.pas文件中的4462--4466行(D5版本),或者4816--4820行(D6版本) ParentForm.ControlState := ParentForm.ControlState + [csDocking]; OldOrient := ParentForm.DockOrientation; ParentForm.DockOrientation := doNoOrient; try inherited WindowProc(Message); finally ParentForm.ControlState := ParentForm.ControlState - [csDocking]; ParentForm.DockOrientation := OldOrient; end; Exit; end; CM_ENTER: Activate; CM_EXIT: Deactivate; CM_VISIBLECHANGED: CMVisibleChanged(Message); end; end; inherited WindowProc(Message); end; { 创建平铺的服务窗体并且把控件停靠进去 } function TCnDockClient.CreateConjoinHostAndDockControl( Control1, Control2: TControl; DockType: TAlign): TCnConjoinDockHostForm; var APanel: TCnConjoinPanel; OldDockWidth, OldDockHeight: Integer; begin { 创建一个TCnConjoinDockHostForm窗体作为TCnConjoinPanel的Parent } Result := TCnConjoinDockHostForm.Create(Application); { 创建一个TCnConjoinPanel,并且把它放在Result上 } APanel := CreateConjoinPanelClass(Result); { Result的位置大小和Control1的相同,所以在调用CreateConjoinHostAndDockControl 的时候要注意:Control1为停靠目标窗体,Control2为停靠源窗体 } Result.BoundsRect := Control1.BoundsRect; { 调整Result的宽度和高度为UndockWidth和UndockHeight } Result.Width := Control1.UndockWidth; Result.Height := Control1.UndockHeight; { 分别把Control1和Control2停靠进APage } OldDockWidth := Control1.LRDockWidth; OldDockHeight := Control1.TBDockHeight; Control1.ManualDock(APanel, nil, alNone); Control1.LRDockWidth := OldDockWidth; Control1.TBDockHeight := OldDockHeight; {============================================================================} OldDockWidth := Control2.LRDockWidth; OldDockHeight := Control2.TBDockHeight; Control2.ManualDock(APanel, nil, DockType); Control2.LRDockWidth := OldDockWidth; Control2.TBDockHeight := OldDockHeight; { 把Result的DockSite属性设置成False } SetDockSite(Result, False); end; { 创建分页的服务窗体并且把控件停靠进去 } function TCnDockClient.CreateTabHostAndDockControl( Control1, Control2: TControl): TCnTabDockHostForm; var APage: TCnTabPageControl; OldDockWidth, OldDockHeight: Integer; begin { 创建一个TCnTabDockHostForm窗体作为TCnTabPageControl的Parent } Result := TCnTabDockHostForm.Create(Application); { 创建一个TCnTabPageControl,并且把它放在Result上 } APage := CreateTabDockClass(Result); { Result的位置大小和Control1的相同,所以在调用CreateTabHostAndDockControl 的时候要注意:Control1为停靠目标窗体,Control2为停靠源窗体 } Result.BoundsRect := Control1.BoundsRect; { 调整Result的宽度和高度为UndockWidth和UndockHeight } Result.Width := Control1.UndockWidth; Result.Height := Control1.UndockHeight; { 分别把Control1和Control2停靠进APage } OldDockWidth := Control1.LRDockWidth; OldDockHeight := Control1.TBDockHeight; Control1.ManualDock(APage, nil, alClient); Control1.LRDockWidth := OldDockWidth; Control1.TBDockHeight := OldDockHeight; {============================================================================} OldDockWidth := Control2.LRDockWidth; OldDockHeight := Control2.TBDockHeight; Control2.ManualDock(APage, nil, alClient); Control2.LRDockWidth := OldDockWidth; Control2.TBDockHeight := OldDockHeight; { 把Result的DockSite属性设置成False } SetDockSite(Result, False); end; procedure TCnDockClient.MakeHideEvent; begin ParentVisible := False; if Assigned(FOnFormHide) then FOnFormHide(Self); end; procedure TCnDockClient.MakeShowEvent; begin if {(not ParentVisible) and }ParentForm.Visible then begin if Assigned(FOnFormShow) then FOnFormShow(Self); ParentVisible := True; end; end; procedure TCnDockClient.SetParentVisible(const Value: Boolean); begin FParentVisible := Value; end; procedure TCnDockClient.DoFloatDockClients(PanelAlign: TAlign); begin if (ParentForm.HostDockSite is TCnDockPanel) and (PanelAlign = ParentForm.HostDockSite.Align) then RestoreChild; end; procedure TCnDockClient.SetBottomDock(const Value: Boolean); begin if not Value then DoFloatDockClients(alBottom); inherited SetBottomDock(Value); end; procedure TCnDockClient.SetEachotherDock(const Value: Boolean); begin if not Value then DoFloatDockEachOther; inherited SetEachotherDock(Value); end; procedure TCnDockClient.SetEnableDock(const Value: Boolean); begin if not Value then begin DoFloatDockClients(alTop); DoFloatDockClients(alBottom); DoFloatDockClients(alLeft); DoFloatDockClients(alRight); DoFloatDockEachOther; end; if ParentForm <> nil then begin if Value then ParentForm.DragKind := dkDock else ParentForm.DragKind := dkDrag; end; inherited SetEnableDock(Value); end; procedure TCnDockClient.SetLeftDock(const Value: Boolean); begin if not Value then DoFloatDockClients(alLeft); inherited SetLeftDock(Value); end; procedure TCnDockClient.SetRightDock(const Value: Boolean); begin if not Value then DoFloatDockClients(alRight); inherited SetRightDock(Value); end; procedure TCnDockClient.SetTopDock(const Value: Boolean); begin if not Value then DoFloatDockClients(alTop); inherited SetTopDock(Value); end; procedure TCnDockClient.DoFloatDockEachOther; begin if (ParentForm.HostDockSite <> nil) and (ParentForm.HostDockSite.Parent is TCnDockableForm) then RestoreChild; end; procedure TCnDockClient.WMSize(var Message: TWMSize); begin { if (ParentForm.HostDockSite <> nil) and (Message.Width * Message.Height <> 0) then begin if ParentForm.DockOrientation = doVertical then // 垂直 ParentForm.LRDockWidth := Message.Width else if ParentForm.DockOrientation = doHorizontal then ParentForm.TBDockHeight := Message.Height else begin if (ParentForm.HostDockSite is TCnDockPanel) and (ParentForm.HostDockSite.FindChildControl(ParentForm.Name) <> nil) then begin if ParentForm.HostDockSite.Align in [alLeft, alRight] then ParentForm.LRDockWidth := Message.Width else ParentForm.TBDockHeight := Message.Height; end; end; end;} inherited; end; procedure TCnDockClient.GetComponentInfo(var AName, Author, Email, Comment: string); begin AName := SCnDockClientName; Author := SCnPack_LuXiaoban; Email := SCnPack_LuXiaobanEmail; Comment := SCnDockClientComment; end; procedure TCnDockClient.Loaded; begin { 设置FConjoinPanelClass } if Assigned(DockStyle) and Assigned(DockStyle.CnConjoinPanelClass) then FConjoinPanelClass := DockStyle.CnConjoinPanelClass else FConjoinPanelClass := DefaultConjoinPanelClass; { 设置FTabDockClass } if Assigned(DockStyle) and Assigned(DockStyle.CnTabDockClass) then FTabDockClass := DockStyle.CnTabDockClass else FTabDockClass := DefaultTabDockClass; inherited Loaded; end; function TCnDockClient.CreateConjoinPanelClass( ConjoinHost: TForm): TCnConjoinPanel; begin Result := nil; TCnConjoinDockHostForm(ConjoinHost).DockClient.Assign(Self); if (FConjoinPanelClass <> nil) and (FConjoinPanelClass <> TCnConjoinPanelClass(ClassType)) then begin Result := FConjoinPanelClass.Create(ConjoinHost); Result.Align := alClient; TCnConjoinDockHostForm(ConjoinHost).DockableControl := Result; TCnConjoinDockHostForm(ConjoinHost).Panel := Result; SetDockSite(Result, True); { 调用DockStyle.AssignConjoinServerOption来设置一些在DockStyle中设置的属性 } DockStyle.AssignConjoinServerOption(TCnConjoinDockHostForm(ConjoinHost).Panel); end; end; function TCnDockClient.CreateTabDockClass( TabHost: TForm): TCnTabPageControl; begin Result := nil; TCnTabDockHostForm(TabHost).DockClient.Assign(Self); if (FTabDockClass <> nil) and (FTabDockClass <> TCnTabDockClass(ClassType)) then begin Result := FTabDockClass.Create(TabHost); Result.Align := alClient; TCnTabDockHostForm(TabHost).DockableControl := Result; TCnTabDockHostForm(TabHost).PageControl := Result; SetDockSite(Result, True); { 调用DockStyle.AssignTabServerOption来设置一些在DockStyle中设置的属性 } DockStyle.AssignTabServerOption(TCnTabDockHostForm(TabHost).PageControl); end; end; procedure TCnDockClient.WMActivate(var Message: TWMActivate); begin if (ParentForm is TCnConjoinDockHostForm) then begin if (Message.Active = WA_INACTIVE) then TCnConjoinPanel(TCnConjoinDockHostForm(ParentForm).Panel).CnDockManager.ActiveControl := nil else begin TCnConjoinPanel(TCnConjoinDockHostForm(ParentForm).Panel).CnDockManager.ActiveControl := GetActiveControl(ParentForm); end; end; end; procedure TCnDockClient.Activate; begin if ParentForm.HostDockSite is TCnCustomDockPanel then begin TCnCustomDockPanel( ParentForm.HostDockSite).CnDockManager.ActiveControl := ParentForm; end; end; procedure TCnDockClient.Deactivate; begin if ParentForm.HostDockSite is TCnCustomDockPanel then begin if TCnCustomDockPanel(ParentForm.HostDockSite).CnDockManager <> nil then TCnCustomDockPanel(ParentForm.HostDockSite).CnDockManager.ActiveControl := nil; end; end; procedure TCnDockClient.DoFormOnClose(Sender: TObject; var Action: TCloseAction); begin if Action = caHide then begin HideDockForm(ParentForm); FParentVisible := True; end; end; procedure TCnDockClient.FormDockDrop(Source: TCnDragDockObject; X, Y: Integer); begin if Assigned(DockStyle) then DockStyle.FormDockDrop(Self, Source, X, Y); end; procedure TCnDockClient.FormDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin FormPositionDockRect(Source); if Assigned(DockStyle) then DockStyle.FormDockOver(Self, Source, X, Y, State, Accept); end; procedure TCnDockClient.FormEndDock(Target: TObject; X, Y: Integer); begin if Assigned(DockStyle) then DockStyle.FormEndDock(Self, Target, X, Y); end; procedure TCnDockClient.FormPositionDockRect(Source: TCnDragDockObject); begin if Assigned(DockStyle) then DockStyle.FormPositionDockRect(Self, Source); end; procedure TCnDockClient.FormStartDock(var Source: TCnDragDockObject); begin if Assigned(DockStyle) then DockStyle.FormStartDock(Self, Source); end; function TCnDockClient.FormUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin if Assigned(DockStyle) then Result := DockStyle.FormUnDock(Self, Newtarget, Client) else Result := False; end; procedure TCnDockClient.FormGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); begin GetWindowRect(ParentForm.Handle, InfluenceRect); InflateRect(InfluenceRect, -4, -4); if Assigned(DockStyle) then DockStyle.FormGetSiteInfo(Source, Self, Client, InfluenceRect, MousePos, CanDock); end; function TCnDockClient.GetLRDockWidth: Integer; begin Result := ParentForm.LRDockWidth; end; function TCnDockClient.GetTBDockHeight: Integer; begin Result := ParentForm.TBDockHeight; end; procedure TCnDockClient.SetLRDockWidth(const Value: Integer); begin if ParentForm.LRDockWidth <> Value then ParentForm.LRDockWidth := Value; end; procedure TCnDockClient.SetTBDockHeight(const Value: Integer); begin if ParentForm.TBDockHeight <> Value then ParentForm.TBDockHeight := Value; end; procedure TCnDockClient.DoNCButtonDown(Message: TWMNCHitMessage; Button: TMouseButton; MouseStation: TMouseStation); begin if Assigned(FOnNCButtonDown) then FOnNCButtonDown(Self, Button, Message.XCursor, Message.YCursor, Message.HitTest, MouseStation); end; procedure TCnDockClient.DoNCButtonUp(Message: TWMNCHitMessage; Button: TMouseButton; MouseStation: TMouseStation); begin if Assigned(FOnNCButtonUp) then FOnNCButtonUp(Self, Button, Message.XCursor, Message.YCursor, Message.HitTest, MouseStation); if Button = mbRight then { 如果是鼠标右键,就弹出菜单 } DoMenuPopup(Message.XCursor, Message.YCursor); end; procedure TCnDockClient.DoNCMouseMove(Message: TWMNCHitMessage; MouseStation: TMouseStation); begin if Assigned(FOnNCMouseMove) then FOnNCMouseMove(Self, Message.XCursor, Message.YCursor, Message.HitTest, MouseStation); end; procedure TCnDockClient.DoNCButtonDblClk(Message: TWMNCHitMessage; Button: TMouseButton; MouseStation: TMouseStation); begin if Assigned(FOnNCButtonDblClk) then FOnNCButtonDblClk(Self, Button, Message.XCursor, Message.YCursor, Message.HitTest, MouseStation); end; procedure TCnDockClient.WMNCLButtonDown(var Message: TWMNCHitMessage); begin DoNCButtonDown(Message, mbLeft, msFloat); GlobalDockClient := Self; if (Message.HitTest = HTCAPTION) and (ParentForm.DragKind = dkDock) and not (csDesigning in ComponentState) and not IsIconic(ParentForm.Handle) then begin { Activate window since we override WM_NCLBUTTON behavior } SetWindowPos(ParentForm.Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE); PostMessage(ParentForm.Handle, WM_NCLBUTTONUP, TMessage(Message).WParam, TMessage(Message).LParam); if ParentForm.Active then CnGlobalDockPresident.BeginDrag(ParentForm, DirectDrag, Integer(DirectDrag) * 2 - 1); Message.Result := 1; end else Message.Result := 0; end; procedure TCnDockClient.WMNCLButtonUp(var Message: TWMNCHitMessage); begin DoNCButtonUp(Message, mbLeft, msFloat); end; procedure TCnDockClient.WMNCMButtonDown(var Message: TWMNCHitMessage); begin DoNCButtonDown(Message, mbMiddle, msFloat); end; procedure TCnDockClient.WMNCMButtonUp(var Message: TWMNCHitMessage); begin DoNCButtonUp(Message, mbMiddle, msFloat); end; procedure TCnDockClient.WMNCRButtonDown(var Message: TWMNCHitMessage); begin DoNCButtonDown(Message, mbRight, msFloat); end; procedure TCnDockClient.WMNCRButtonUp(var Message: TWMNCHitMessage); begin DoNCButtonUp(Message, mbRight, msFloat); end; procedure TCnDockClient.WMNCMOUSEMOVE(var Message: TWMNCHitMessage); begin DoNCMouseMove(Message, msFloat); end; procedure TCnDockClient.WMNCLButtonDblClk(var Message: TWMNCHitMessage); begin DoNCButtonDblClk(Message, mbLeft, msFloat); end; procedure TCnDockClient.WMNCMButtonDblClk(var Message: TWMNCHitMessage); begin DoNCButtonDblClk(Message, mbMiddle, msFloat); end; procedure TCnDockClient.WMNCRButtonDblClk(var Message: TWMNCHitMessage); begin DoNCButtonDblClk(Message, mbRight, msFloat); end; procedure TCnDockClient.SetNCPopupMenu(const Value: TPopupMenu); begin FNCPopupMenu := Value; end; procedure TCnDockClient.DoMenuPopup(X, Y: Integer); begin if FNCPopupMenu <> nil then begin FNCPopupMenu.PopupComponent := ParentForm; FNCPopupMenu.Popup(X, Y); end; end; procedure TCnDockClient.DoPaintDockGrabber( Canvas: TCanvas; Control: TControl; const ARect: TRect); begin if Assigned(FOnPaintDockGrabber) then FOnPaintDockGrabber(Canvas, Control, ARect); end; procedure TCnDockClient.DoPaintDockSplitter(Canvas: TCanvas; Control: TControl; const ARect: TRect); begin if Assigned(FOnPaintDockSplitter) then FOnPaintDockSplitter(Canvas, Control, ARect); end; procedure TCnDockClient.FormGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); begin if Assigned(DockStyle) then DockStyle.FormGetDockEdge(Self, Source, MousePos, DropAlign) else DropAlign := alNone; end; procedure TCnDockClient.DoFormShowHint(HTFlag: Integer; var HintStr: string; var CanShow: Boolean); begin if Assigned(FOnFormShowHint) then FOnFormShowHint(HTFlag, HintStr, CanShow); end; procedure TCnDockClient.SetCurrentDockSite(const Value: TWinControl); begin FCurrentDockSite := Value; end; procedure TCnDockClient.SetLastDockSite(const Value: TWinControl); begin FLastDockSite := Value; end; procedure TCnDockClient.SetVSPaneWidth(const Value: Integer); begin FVSPaneWidth := Value; end; procedure TCnDockClient.RestoreChild; begin DockStyle.RestoreClient(Self); end; procedure TCnDockClient.SetUnDockLeft(const Value: Integer); begin FUnDockLeft := Value; end; procedure TCnDockClient.SetUnDockTop(const Value: Integer); begin FUnDockTop := Value; end; procedure TCnDockClient.HideParentForm; begin HideDockForm(ParentForm); end; procedure TCnDockClient.ShowParentForm; begin ShowDockForm(ParentForm); end; function TCnDockClient.GetDockState: Integer; begin Result := DS_Unknow; if DockStyle <> nil then Result := DockStyle.GetDockState(Self); end; procedure TCnDockClient.CMVisibleChanged(var Message: TMessage); begin end; procedure TCnDockClient.SetCanFloat(const Value: Boolean); begin FCanFloat := Value; end; procedure TCnDockClient.SetRelativeServer(const Value: TCnDockServer); begin if (csDesigning in ComponentState){ or (csLoading in ComponentState) }then begin if Value <> nil then Value.FreeNotification(ParentForm); end; FRelativeServer := Value; end; procedure TCnDockClient.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation = opRemove) and (AComponent = FRelativeServer) then // 如果是释放,就要把FRelativeServer属性设成nil。 FRelativeServer := nil; end; procedure TCnDockClient.SetDockLevel(const Value: Integer); begin if not ParentForm.Floating then begin if FDockLevel <> Value then DoFloatForm(ParentForm); end; FDockLevel := Value; end; procedure TCnDockClient.SetEnableCloseBtn(const Value: Boolean); begin FEnableCloseBtn := Value; end; { TCnDockableForm } constructor TCnDockableForm.Create(AOwner: TComponent); begin inherited Create(AOwner); DragKind := dkDock; FDockClient := TCnDockClient.Create(self); CnGlobalDockPresident.DockableFormList.Add(Self); FFloatingChild := nil; TBDockHeight := FDockClient.TBDockHeight; LRDockWidth := FDockClient.LRDockWidth; end; destructor TCnDockableForm.Destroy; var Index: Integer; begin if CnGlobalDockPresident <> nil then begin Index := CnGlobalDockPresident.DockableFormList.IndexOf(Self); if Index <> -1 then CnGlobalDockPresident.DockableFormList.Delete(Index); end; if DockClient.LastDockSite is TCnDockPanel then TCnDockPanel(DockClient.LastDockSite).CnDockManager.RemoveControl(Self); inherited Destroy; FFloatingChild := nil; end; procedure TCnDockableForm.DoClose(var Action: TCloseAction); var i: Integer; begin if DockableControl.DockClientCount = 1 then begin { 保存这个客户,可能以后还要重新停靠到服务器上面 } FFloatingChild := DockableControl.DockClients[0]; { 如果HostDockSite不为空,说明以后要把它停靠到服务器上面,为了防止闪烁,先把 FFloatingChild隐藏掉,然后等到重新停靠后再显示出来,就可以防止闪烁,显示的 函数见CnDockSupportControl.pas的TCnCustomDockPanel.WndProc函数 } if HostDockSite <> nil then FFloatingChild.Visible := False; { 如果停靠客户的个数为1,就把她拖出,并且释放本身 } DoFloat(Self, DockableControl.DockClients[0]); Action := caFree; end else if DockableControl.DockClientCount = 0 then { 如果已经没有停靠客户,就把窗体释放掉 } Action := caFree else begin { 否则就隐藏 } Action := caHide; { 如果查找到DockableControl中的客户有一个是FUnDockControl,就不隐藏自己 } if (FUnDockControl <> nil) and (DockableControl.DockClientCount = 2) then begin for i := 0 to DockableControl.DockClientCount - 1 do if FUnDockControl = DockableControl.DockClients[i] then begin Action := caNone; Break; end; end; end; if (HostDockSite is TCnDockPanel) and (HostDockSite.VisibleDockClientCount = 1) and (FFloatingChild = nil) then // 如果停靠服务器是TDockServer中的DockPanel,就调用ShowDockPanel函数。 TCnDockPanel(HostDockSite).ShowDockPanel(False, Self); inherited DoClose(Action); FUnDockControl := nil; end; function TCnDockableForm.GetDockableControl: TWinControl; begin Result := FDockableControl; end; procedure TCnDockableForm.SetDockableControl(const Value: TWinControl); begin FDockableControl := Value; end; procedure TCnDockableForm.SetUnDockControl(const Value: TControl); begin FUnDockControl := Value; end; { TCnConjoinDockHostForm } constructor TCnConjoinDockHostForm.Create(AOwner: TComponent); begin inherited Create(AOwner); BorderStyle := ConjoinDockHostBorderStyle; end; procedure TCnConjoinDockHostForm.DoClose(var Action: TCloseAction); begin inherited DoClose(Action); end; { TCnTabDockHostForm } constructor TCnTabDockHostForm.Create(AOwner: TComponent); begin inherited Create(AOwner); BorderStyle := TabDockHostBorderStyle; end; destructor TCnTabDockHostForm.Destroy; begin inherited; end; function TCnTabDockHostForm.GetActiveDockForm: TForm; begin if PageControl.ActivePage.ControlCount = 1 then Result := TForm(PageControl.ActivePage.Controls[0]) else Result := nil; end; { TCnConjoinPanel } constructor TCnConjoinPanel.Create(AOwner: TComponent); begin inherited Create(AOwner); Parent := TWinControl(AOwner); DockClient := TCnConjoinDockHostForm(AOwner).DockClient; Align := alClient; BevelOuter := bvNone; DoubleBuffered := True; ParentFont := False; Caption := ''; end; procedure TCnConjoinPanel.DockDrop(Source: TDragDockObject; X, Y: Integer); begin if Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0 then begin if Source.Control is TForm then begin { 下面两行代码是为了防止窗体的标题栏和窗体上面的控件的内容被清空 } ParentForm.ActiveControl := nil; TForm(Source.Control).ActiveControl := nil; { ------------------------------------------------------------------------ } SetDockSite(TForm(Source.Control), False); if TForm(Source.Control).FormStyle = fsStayOnTop then TForm(Parent).FormStyle := fsStayOnTop; end; UpdateCaption(nil); end; end; procedure TCnConjoinPanel.DoDockOver(Source: TDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin { 如果停靠客户是TDockableForm,并且停靠客户上面有TDockClient控件,就同意停靠 } Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign); end; function TCnConjoinPanel.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin ParentForm.FUnDockControl := Client; { 如果停靠客户是TDockableForm,就还原停靠客户的DockSite属性为True } if not (Client is TCnDockableForm) then SetDockSite(TForm(Client), True); if (VisibleDockClientCount = 1) or (DockClientCount <= 2){ and (NewTarget <> Self) }then PostMessage(Parent.Handle, WM_CLOSE, 0, 0); UpdateCaption(Client); Result := Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0; end; function TCnConjoinPanel.GetParentForm: TCnConjoinDockHostForm; begin Result := TCnConjoinDockHostForm(Parent); end; procedure TCnConjoinPanel.GetSiteInfo(Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); begin CanDock := IsDockable(Self, Client); if CanDock then begin GetWindowRect(Handle, InfluenceRect); end; end; procedure TCnConjoinPanel.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); begin inherited CustomDockDrop(Source, X, Y); if Source.Control is TForm then begin { 下面代码是为了防止窗体的标题栏和窗体上面的控件的内容被清空 } ParentForm.ActiveControl := nil; if TForm(Source.Control).FormStyle = fsStayOnTop then TForm(Parent).FormStyle := fsStayOnTop; end; UpdateCaption(nil); end; procedure TCnConjoinPanel.CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin inherited CustomDockOver(Source, X, Y, State, Accept); { 如果停靠客户是TDockableForm,并且停靠客户上面有TDockClient控件,就同意停靠 } Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign); end; procedure TCnConjoinPanel.CustomEndDock(Target: TObject; X, Y: Integer); begin inherited CustomEndDock(Target, X, Y);; end; procedure TCnConjoinPanel.CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); begin CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign); inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock); end; procedure TCnConjoinPanel.CustomPositionDockRect(Source: TCnDragDockObject; X, Y: Integer); begin inherited CustomPositionDockRect(Source, X, Y); end; procedure TCnConjoinPanel.CustomStartDock(var Source: TCnDragDockObject); begin ParentForm.FUnDockControl := nil; inherited CustomStartDock(Source); end; function TCnConjoinPanel.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; begin ParentForm.FUnDockControl := Client; { 如果停靠客户是TDockableForm,就还原停靠客户的DockSite属性为True } if not (Client is TCnDockableForm) then SetDockSite(TForm(Client), True); if ((VisibleDockClientCount = 1) or (DockClientCount <= 2)) and (NewTarget <> ParentForm.DockableControl) then begin PostMessage(Parent.Handle, WM_CLOSE, 0, 0); end; UpdateCaption(Client); Result := inherited CustomUnDock(Source, NewTarget, Client); end; procedure TCnConjoinPanel.ReloadDockedControl( const AControlName: string; var AControl: TControl); begin AControl := Cn_FindDockFormWithName(AControlName); end; procedure TCnConjoinPanel.CMUnDockClient(var Message: TCMUnDockClient); begin inherited; { 如果有两个停靠客户,就要释放ParentForm } if (DockClientCount = 2) and (VisibleDockClientCount = 1) then PostMessage(ParentForm.Handle, WM_CLOSE, 0, 0); if VisibleDockClientCount <= 2 then CnDockFormControl.UpdateCaption(Self, Message.Client); if UseDockManager and (CnDockManager <> nil) then CnDockManager.ResetBounds(True); end; { TCnTabPageControl } procedure TCnTabPageControl.AdjustClientRect(var Rect: TRect); begin inherited AdjustClientRect(Rect); case TabPosition of tpLeft: Inc(Rect.Left, 2); tpRight: Dec(Rect.Right, 2); tpBottom: begin Dec(Rect.Top, 1); Dec(Rect.Bottom, 2); end; end; end; constructor TCnTabPageControl.Create(AOwner: TComponent); begin inherited Create(AOwner); Parent := TWinControl(AOwner); DockClient := FindDockClient(Parent); SetDockSite(Self, True); PopupMenu := DockPageControlPopupMenu; HotTrack := DockPageControlHotTrack; DoubleBuffered := True; Caption := ''; FVersion := $00040000; end; var TabPageStreamEndFlag: Integer = -10; destructor TCnTabPageControl.Destroy; begin SetDockSite(Self, False); inherited Destroy; end; procedure TCnTabPageControl.DockDrop(Source: TDragDockObject; X, Y: Integer); begin if Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0 then begin if Source.Control is TForm then begin { 下面一行代码是为了防止窗体的标题栏和窗体上面的控件的内容被清空 } TForm(Source.Control).ActiveControl := nil; SetDockSite(TWinControl(Source.Control), False); if TForm(Source.Control).FormStyle = fsStayOnTop then TForm(Parent).FormStyle := fsStayOnTop; end; UpdateCaption(nil); end; end; function TCnTabPageControl.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin { 如果停靠客户是TDockableForm,就还原停靠客户的DockSite属性为True } if not (Client is TCnDockableForm) then SetDockSite(TForm(Client), True); if (VisibleDockClientCount = 1) or (DockClientCount <= 2){ and (NewTarget <> Self) }then PostMessage(Parent.Handle, WM_CLOSE, 0, 0); UpdateCaption(Client); Result := Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0; end; function TCnTabPageControl.GetParentForm: TCnTabDockHostForm; begin Result := TCnTabDockHostForm(Parent); end; procedure TCnTabPageControl.LoadFromStream(Stream: TStream); var i, Count, NameLen, SheetVisible, ActiveSheetIndex: Integer; ControlName: string; AControl: TControl; begin try Stream.Read(i, Sizeof(i)); { 从流中读出标签的个数,并且依次从流中读出她们的信息 } Stream.Read(Count, Sizeof(Count)); for i := 0 to Count - 1 do begin ControlName := ''; { 读出名字的长度 } Stream.Read(NameLen, SizeOf(NameLen)); if NameLen > 0 then begin { 读出名字的字符串 } SetLength(ControlName, NameLen); Stream.Read(Pointer(ControlName)^, NameLen * SizeOf(Char)); end; if ControlName <> '' then begin { 找到名字为ControlName的停靠窗体,并且把她停靠进TCnTabPageControl } ReloadDockedControl(ControlName, AControl); if AControl <> nil then AControl.ManualDock(Self, nil, alClient); end; { 读出标签的Visible } Stream.Read(SheetVisible, SizeOf(SheetVisible)); DockClients[i].Visible := Boolean(SheetVisible); end; { 读出被激活的标签 } Stream.Read(ActiveSheetIndex, SizeOf(ActiveSheetIndex)); ActivePageIndex := ActiveSheetIndex; finally end; end; procedure TCnTabPageControl.SaveToStream(Stream: TStream); var i, Count, NameLen, SheetVisible, ActiveSheetIndex: Integer; ControlName: string; CurrentControl: TControl; begin { 写入流的版本 } Stream.Write(FVersion, SizeOf(FVersion)); Count := PageCount; { 写入TabSheet的个数 } Stream.Write(Count, SizeOf(Count)); for i := 0 to PageCount - 1 do begin CurrentControl := Pages[i].Controls[0]; ControlName := CurrentControl.Name; NameLen := Length(ControlName); { 写入名字的长度 } Stream.Write(NameLen, SizeOf(NameLen)); { 写入名字 } if NameLen > 0 then Stream.Write(Pointer(ControlName)^, NameLen * SizeOf(Char)); SheetVisible := 0; if (Self is TCnVSNETTabPageControl) and (ParentForm.HostDockSite is TCnDockPanel) then SheetVisible := Integer(TCnVSNETDockTabSheet(Pages[i]).OldVisible) else SheetVisible := SheetVisible + Integer(CurrentControl.Visible); { 写入是否可见 } Stream.Write(SheetVisible, SizeOf(SheetVisible)); end; ActiveSheetIndex := ActivePageIndex; { 写入激活的TabSheet的索引 } Stream.Write(ActiveSheetIndex, SizeOf(ActiveSheetIndex)); { 写入结束标志 } Stream.Write(TabPageStreamEndFlag, SizeOf(TabPageStreamEndFlag)); end; procedure TCnTabPageControl.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); var DragDockObject: TDragDockObject; begin if Source.DropAlign in [alClient, alNone] then begin DragDockObject := TDragDockObject.Create(Source.Control); try DragDockObject.DockRect := Source.DockRect; DragDockObject.Control := Source.Control; Perform(CM_DOCKCLIENT, Integer(DragDockObject), Integer(SmallPoint(X, Y))); UpdateCaption(nil); finally DragDockObject.Free; end; end else begin inherited CustomDockDrop(Source, X, Y); end; if Source.Control is TForm then begin { 下面一行代码是为了防止窗体的标题栏和窗体上面的控件的内容被清空 } TForm(Source.Control).ActiveControl := nil; { ------------------------------------------------------------------------ } SetDockSite(TForm(Source.Control), False); end; end; procedure TCnTabPageControl.CustomDockOver(Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var ARect: TRect; begin inherited CustomDockOver(Source, X, Y, State, Accept); { 如果停靠客户上面有TDockClient控件,就同意停靠 } Accept := IsDockable(Self, Source.Control, Source.DropOnControl, Source.DropAlign); // 画停靠客户的预览停靠位置 if Accept then begin ComputeDockingRect(self, ARect, Point(ClientWidth div 2, ClientHeight div 2)); Source.DockRect := ARect; end; end; procedure TCnTabPageControl.CustomEndDock(Target: TObject; X, Y: Integer); begin inherited CustomEndDock(Target, X, Y); end; procedure TCnTabPageControl.CustomGetSiteInfo(Source: TCnDragDockObject; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); begin inherited CustomGetSiteInfo(Source, Client, InfluenceRect, MousePos, CanDock); CanDock := IsDockable(Self, Client, Source.DropOnControl, Source.DropAlign); end; procedure TCnTabPageControl.CustomPositionDockRect( Source: TCnDragDockObject; X, Y: Integer); begin inherited CustomPositionDockRect(Source, X, Y); end; procedure TCnTabPageControl.CustomStartDock(var Source: TCnDragDockObject); begin inherited CustomStartDock(Source); end; function TCnTabPageControl.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; begin { 如果停靠客户是TDockableForm,就还原停靠客户的DockSite属性为True } if not (Client is TCnDockableForm) then SetDockSite(TForm(Client), True); if (VisibleDockClientCount = 1) or (DockClientCount <= 2) then PostMessage(Parent.Handle, WM_CLOSE, 0, 0); UpdateCaption(Client); Result := Perform(CM_UNDOCKCLIENT, Integer(NewTarget), Integer(Client)) = 0; end; procedure TCnTabPageControl.ReloadDockedControl( const AControlName: string; var AControl: TControl); begin AControl := Cn_FindDockFormWithName(AControlName); end; procedure TCnTabPageControl.CustomGetDockEdge(Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); var ARect: TRect; begin ARect := Source.DockRect; DropAlign := ComputeDockingRect(Source.Control, ARect, MousePos); end; { TCnBasicDockStyle } constructor TCnBasicDockStyle.Create(AOwner: TComponent); begin inherited Create(AOwner); { 设置一些默认的类的引用 } CnDockPanelClass := DefaultDockPanelClass; CnDockSplitterClass := DefaultDockSplitterClass; CnConjoinPanelClass := DefaultConjoinPanelClass; CnTabDockClass := DefaultTabDockClass; CnDockPanelTreeClass := DefaultDockTreeClass; CnDockPanelZoneClass := DefaultDockZoneClass; CnConjoinPanelTreeClass := DefaultDockTreeClass; CnConjoinPanelZoneClass := DefaultDockZoneClass; FCnConjoinServerOptionClass := TCnBasicConjoinServerOption; FCnTabServerOptionClass := TCnBasicTabServerOption; FDockBaseControlList := TList.Create; if AOwner is TCustomForm then FParentForm := TForm(AOwner) else FParentForm := nil; if not (csDesigning in ComponentState) then begin { 保存老的窗口过程 } FOldWindowProc := FParentForm.WindowProc; { 重载窗口过程 } FParentForm.WindowProc := ParentFormWindowProc; end; end; function TCnBasicDockStyle.GetControlName: string; begin Result := gs_CnDockStyleName; end; function TCnBasicDockStyle.GetDockStyleVersion: string; begin Result := gs_CnDockStyleVersion; end; procedure TCnBasicDockStyle.SetDockBaseControl(IsCreate: Boolean; DockBaseControl: TCnDockBaseControl); begin { 没有事做 } end; procedure TCnBasicDockStyle.FormDockDrop(DockClient: TCnDockClient; Source: TCnDragDockObject; X, Y: Integer); var ARect,DRect: TRect; DockType: TAlign; Host: TCustomForm; APanelDock: TWinControl; begin if IsDockable(DockClient.ParentForm, Source.Control, Source.DropOnControl, Source.DropAlign) then begin Host := nil; { 锁住Windows桌面 } if not IsLoading then Cn_LockWindow(nil); try // 调用ComputeDockingRect函数知道停靠的类型 with DockClient do begin DockType := ComputeDockingRect(DockClient.ParentForm, ARect, Point(X, Y)); if (ParentForm.HostDockSite is TCnDockPanel) then begin // 如果停靠服务器是TDockPanel,就停靠在TDockServer的DockPanel上。 if DockType = alClient then begin // 如果停靠类型是alClient if Source.Control is TCnTabDockHostForm then begin // 如果停靠客户是TCnTabDockHostForm, // 就先把Parent停靠到TCnTabDockHostForm中, // 再把TCnTabDockHostForm停靠到TCnDockPanel中。 APanelDock := ParentForm.HostDockSite; ARect := ParentForm.BoundsRect; ParentForm.ManualDock(TCnTabDockHostForm(Source.Control).PageControl, nil, alClient); TCnTabDockHostForm(Source.Control).PageControl.ActivePage.PageIndex := 0; Source.Control.BoundsRect := ARect; Source.Control.ManualDock(APanelDock, nil, alClient); if ParentForm.FormStyle = fsStayOnTop then TForm(Source.Control).FormStyle := fsStayOnTop; end else begin // 否则就创建TCnTabDockHostForm, // 把把Parent停靠到TCnTabDockHostForm中, // 再把TCnTabDockHostForm停靠到TCnDockPanel中。 APanelDock := ParentForm.HostDockSite; DRect.TopLeft := ParentForm.HostDockSite.ClientToScreen(Point(0, 0)); Host := CreateTabHostAndDockControl(ParentForm, Source.Control); SetDockSite(ParentForm, False); SetDockSite(TWinControl(Source.Control), False); Host.Top := DRect.Top; Host.Left := DRect.Left; Host.ManualDock(APanelDock, nil, alClient); Host.Visible := True; end; end else begin // 如果停靠类型不是alClient, // 就把停靠窗体停靠到TCnDockPanel. DRect := ParentForm.HostDockSite.BoundsRect; Source.Control.ManualDock(ParentForm.HostDockSite, nil, DockType); ParentForm.HostDockSite.BoundsRect := DRect; end; Exit; end; // 创建分页的服务窗体 if DockType = alClient then begin Host := CreateTabHostAndDockControl(ParentForm, Source.Control); SetDockSite(ParentForm, False); SetDockSite(TWinControl(Source.Control), False); Host.Visible := True; end // 创建平铺的服务窗体 else if DockType <> alNone then begin Host := CreateConjoinHostAndDockControl(ParentForm, Source.Control, DockType); SetDockSite(ParentForm, False); SetDockSite(TWinControl(Source.Control), False); Host.Visible := True; end; if Host <> nil then begin Host.LRDockWidth := Source.Control.LRDockWidth; Host.TBDockHeight := Source.Control.TBDockHeight; end; end; finally { 解锁Windows桌面 } if not IsLoading then Cn_UnLockWindow; end; end; end; procedure TCnBasicDockStyle.FormDockOver(DockClient: TCnDockClient; Source: TCnDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var ARect: TRect; begin with DockClient do begin Accept := EnableDock and EachOtherDock and IsDockable(ParentForm.HostDockSite, Source.Control, Source.DropOnControl, Source.DropAlign); if Accept and (State = dsDragMove) and (ComputeDockingRect(ParentForm, ARect, Point(X, Y)) <> alNone) then Source.DockRect := ARect; end; end; procedure TCnBasicDockStyle.FormEndDock(DockClient: TCnDockClient; Target: TObject; X, Y: Integer); begin { 没事做 } end; procedure TCnBasicDockStyle.FormPositionDockRect(DockClient: TCnDockClient; Source: TCnDragDockObject); var NewWidth, NewHeight: Integer; TempX, TempY: Double; begin with Source do begin if (DragTarget = nil) or (not TCnWinControlAccess(DragTarget).UseDockManager) then begin NewWidth := Control.UndockWidth; NewHeight := Control.UndockHeight; // Drag position for dock rect is scaled relative to control's click point. TempX := DragPos.X - ((NewWidth) * MouseDeltaX); TempY := DragPos.Y - ((NewHeight) * MouseDeltaY); with DockRect do begin Left := Round(TempX); Top := Round(TempY); Right := Left + NewWidth; Bottom := Top + NewHeight; end; { Allow DragDockObject final say on this new dock rect } AdjustDockRect(DockRect); end else begin GetWindowRect(TargetControl.Handle, DockRect); if TCnWinControlAccess(DragTarget).UseDockManager then begin if TargetControl is TCnCustomDockPanel then begin if (TCnCustomDockPanel(DragTarget).CnDockManager <> nil) then TCnCustomDockPanel(DragTarget).CnDockManager.PositionDockRect(Control, DropOnControl, DropAlign, DockRect); end; end; end; end; end; procedure TCnBasicDockStyle.GetComponentInfo(var AName, Author, Email, Comment: string); begin // 基类不干 end; procedure TCnBasicDockStyle.FormStartDock(DockClient: TCnDockClient; var Source: TCnDragDockObject); begin { 没事做 } end; function TCnBasicDockStyle.FormUnDock(DockClient: TCnDockClient; NewTarget: TWinControl; Client: TControl): Boolean; begin Result := True; end; procedure TCnBasicDockStyle.FormGetSiteInfo(Source: TCnDragDockObject; DockClient: TCnDockClient; Client: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); begin with DockClient do CanDock := EnableDock and EachOtherDock and IsDockable(ParentForm, Client, Source.DropOnControl, Source.DropAlign); end; function TCnBasicDockStyle.DockClientWindowProc(DockClient: TCnDockClient; var Message: TMessage): Boolean; begin { 默认要执行原来的消息过程 } Result := False; end; procedure TCnBasicDockStyle.FormGetDockEdge(DockClient: TCnDockClient; Source: TCnDragDockObject; MousePos: TPoint; var DropAlign: TAlign); begin DropAlign := TCnControlAccess(DockClient.ParentForm).GetDockEdge(MousePos); end; procedure TCnBasicDockStyle.SetConjoinServerOption( const Value: TCnBasicConjoinServerOption); begin FCnConjoinServerOption.Assign(Value); end; procedure TCnBasicDockStyle.SetTabServerOption( const Value: TCnBasicTabServerOption); begin FCnTabServerOption.Assign(Value); end; procedure TCnBasicDockStyle.CreateConjoinServerOption( var Option: TCnBasicConjoinServerOption); begin Option := TCnBasicConjoinServerOption.Create(Self); end; procedure TCnBasicDockStyle.CreateTabServerOption( var Option: TCnBasicTabServerOption); begin Option := TCnBasicTabServerOption.Create(Self); end; procedure TCnBasicDockStyle.CreateServerOption; begin if FCnConjoinServerOption = nil then FCnConjoinServerOption := FCnConjoinServerOptionClass.Create(Self); if FCnTabServerOption = nil then FCnTabServerOption := FCnTabServerOptionClass.Create(Self); end; function TCnBasicDockStyle.GetConjoinServerOption: TCnBasicConjoinServerOption; begin Result := FCnConjoinServerOption; end; function TCnBasicDockStyle.GetTabServerOption: TCnBasicTabServerOption; begin Result := FCnTabServerOption; end; procedure TCnBasicDockStyle.FreeServerOption; begin if FCnConjoinServerOption <> nil then FCnConjoinServerOption.Free; if FCnTabServerOption <> nil then FCnTabServerOption.Free; end; destructor TCnBasicDockStyle.Destroy; begin if not (csDesigning in ComponentState) then begin if @FOldWindowProc <> nil then FParentForm.WindowProc := FOldWindowProc; FOldWindowProc := nil; end; FDockBaseControlList.Free; FreeServerOption; inherited Destroy; end; procedure TCnBasicDockStyle.AssignConjoinServerOption( APanel: TCnCustomDockPanel); begin APanel.CnDockManager.GrabberSize := ConjoinServerOption.GrabbersSize; APanel.CnDockManager.SplitterWidth := ConjoinServerOption.SplitterWidth; end; procedure TCnBasicDockStyle.AssignTabServerOption( APage: TCnTabPageControl); begin APage.HotTrack := TabServerOption.HotTrack; APage.TabPosition := TabServerOption.TabPosition; end; procedure TCnBasicDockStyle.Loaded; begin inherited; end; procedure TCnBasicDockStyle.AfterConstruction; begin inherited AfterConstruction; CreateServerOption; end; procedure TCnBasicDockStyle.ParentFormWindowProc(var Message: TMessage); begin if not (csDesigning in ComponentState) then begin if Assigned(FOldWindowProc) then FOldWindowProc(Message); end; end; procedure TCnBasicDockStyle.AddDockBaseControl( ADockBaseControl: TCnDockBaseControl); begin if ADockBaseControl = nil then Exit; if FDockBaseControlList.IndexOf(ADockBaseControl) = -1 then begin FDockBaseControlList.Add(ADockBaseControl); ConjoinServerOption.ResetDockControlOption; TabServerOption.ResetDockControlOption; end; end; procedure TCnBasicDockStyle.RemoveDockBaseControl( ADockBaseControl: TCnDockBaseControl); begin if ADockBaseControl = nil then Exit; FDockBaseControlList.Remove(ADockBaseControl); end; procedure TCnBasicDockStyle.ResetDockControlConjoinOption; begin end; procedure TCnBasicDockStyle.ResetDockControlTabOption; begin end; function TCnBasicDockStyle.GetDockBaseControlListCount: Integer; begin Result := FDockBaseControlList.Count; end; function TCnBasicDockStyle.GetDockBaseControlLists( Index: Integer): TCnDockBaseControl; begin Result := FDockBaseControlList[Index]; end; function TCnBasicDockStyle.DockServerWindowProc(DockServer: TCnDockServer; var Message: TMessage): Boolean; begin { 默认要执行原来的消息过程 } Result := False; end; function TCnBasicDockStyle.GetDockState( DockClient: TCnDockClient): Integer; begin Result := DS_Unknow; if (DockClient <> nil) and (DockClient.ParentForm <> nil) then begin if DockClient.ParentForm.Floating then Result := DS_Floating else Result := DS_Docking; end; end; function TCnBasicDockStyle.CanSetBottomDocked(ADockBaseControl: TCnDockBaseControl): Boolean; begin Result := True; end; function TCnBasicDockStyle.CanSetEachOtherDocked(ADockBaseControl: TCnDockBaseControl): Boolean; begin Result := True; end; function TCnBasicDockStyle.CanSetEnableDocked(ADockBaseControl: TCnDockBaseControl): Boolean; begin Result := True; end; function TCnBasicDockStyle.CanSetLeftDocked(ADockBaseControl: TCnDockBaseControl): Boolean; begin Result := True; end; function TCnBasicDockStyle.CanSetRightDocked(ADockBaseControl: TCnDockBaseControl): Boolean; begin Result := True; end; function TCnBasicDockStyle.CanSetTopDocked(ADockBaseControl: TCnDockBaseControl): Boolean; begin Result := True; end; procedure TCnBasicDockStyle.ResetCursor(Source: TCnDragDockObject); begin if (Source.TargetControl = nil) and (Source.Control <> nil) and (Source.Control.Floating) then Windows.SetCursor(Screen.Cursors[crDefault]) else if (Source.TargetControl = nil) and (not GlobalDockClient.CanFloat) then Windows.SetCursor(Screen.Cursors[crNo]) else Windows.SetCursor(Screen.Cursors[crDefault]); end; procedure TCnBasicDockStyle.HideDockForm(ADockClient: TCnDockClient); begin { 调用窗体上的TCnDockClient的MakeHideEvent函数 } if ADockClient <> nil then begin ADockClient.ParentForm.Visible := False; ADockClient.MakeHideEvent; end; end; procedure TCnBasicDockStyle.ShowDockForm(ADockClient: TCnDockClient); begin { 调用窗体上的TCnDockClient的MakeShowEvent函数 } if ADockClient <> nil then begin ADockClient.ParentForm.Visible := True; ADockClient.MakeShowEvent; end; end; function TCnBasicDockStyle.GetDockFormVisible(ADockClient: TCnDockClient): Boolean; begin Result := True; if ADockClient <> nil then begin if ADockClient.ParentForm.Visible then begin if ADockClient.ParentForm.HostDockSite <> nil then begin if ADockClient.ParentForm.HostDockSite is TCnDockPanel then Result := ADockClient.ParentForm.HostDockSite.Width * ADockClient.ParentForm.HostDockSite.Height > 0 else Result := GetFormVisible(ADockClient.ParentForm.HostDockSite.Parent); end; end else Result := False; end; end; procedure TCnBasicDockStyle.RestoreClient(DockClient: TCnDockClient); var TmpLastDockSite: TWinControl; TmpUnDockLeft, TmpUnDockTop: Integer; i: Integer; ADockClient: TCnDockClient; ADockServer: TCnDockServer; ARect: TRect; procedure DoFloatParentForm; begin with DockClient do begin if (ParentForm.HostDockSite <> nil) then begin ARect := Bounds(TmpUnDockLeft, TmpUnDockTop, ParentForm.UndockWidth, ParentForm.UndockHeight); ParentForm.ManualFloat(ARect); if (ParentForm.Left <> ARect.Left) or (ParentForm.Top <> ARect.Top) then begin ParentForm.Left := ARect.Left; ParentForm.Top := ARect.Top; end; end; end; end; begin if DockClient = nil then Exit; if not DockClient.CanFloat then Exit; with DockClient do begin { 必须是能够符合停靠条件的才行 } if not EnableDock then Exit; if LastDockSite is TCnDockPanel then begin with TCnDockPanel(LastDockSite) do begin { 对于停靠客户 } if ((not LeftDock) and (Align = alLeft)) or ((not RightDock) and (Align = alRight)) or ((not TopDock) and (Align = alTop)) or ((not BottomDock) and (Align = alBottom)) then begin DoFloatParentForm; Exit; end; { 对于停靠服务器 } ADockServer := DockServer; if ADockServer <> nil then if (not ADockServer.EnableDock) or ((not ADockServer.LeftDock) and (Align = alLeft)) or ((not ADockServer.RightDock) and (Align = alRight)) or ((not ADockServer.TopDock) and (Align = alTop)) or ((not ADockServer.BottomDock) and (Align = alBottom)) then begin DoFloatParentForm; Exit; end; end; end; if ParentForm is TCnConjoinDockHostForm then begin with TCnConjoinDockHostForm(ParentForm).Panel do begin for i := DockClientCount - 1 downto 0 do begin ADockClient := FindDockClient(DockClients[i]); if (ADockClient <> nil) and (ADockClient.LastDockSite is TCnDockPanel) then ADockClient.RestoreChild; end; end; Exit; end; { 保存原先的LastDockSite,因为在调用一些函数后会覆盖掉原先的LastDockSite } TmpLastDockSite := LastDockSite; TmpUnDockLeft := UnDockLeft; TmpUnDockTop := UnDockTop; { 重新设置DockClient的一些属性 } ResetDockClient(DockClient, nil); DoFloatParentForm; if TmpLastDockSite is TCnDockPanel then begin with TCnDockPanel(TmpLastDockSite) do begin if UseDockManager and (CnDockManager <> nil) then begin if not CnDockManager.HasZoneWithControl(ParentForm) then Exit; DisableAlign; try { 调用ParentForm的Dock函数把ParentForm停靠到LastDockSite中 } ParentForm.Dock(TmpLastDockSite, Rect(0,0,0,0)); { 由于在LastDockSite的CnDockManager接口中已经有一个ParentForm的Zone, 所以只要把它show出来就可以了 } CnDockManager.ShowControl(ParentForm); { 下面一行代码是为了防止窗体的标题栏和窗体上面的控件的内容被清空 } ParentForm.ActiveControl := nil; SetDockSite(ParentForm, False); { 使ParentForm获得焦点 } if ParentForm.Visible and ParentForm.CanFocus then ParentForm.SetFocus; ShowDockPanel(True, ParentForm, sdfDockPanel); finally EnableAlign; end; end; end; end; end; end; { TCnAdvDockStyle } function TCnAdvDockStyle.DockClientWindowProc(DockClient: TCnDockClient; var Message: TMessage): Boolean; begin if (DockClient <> nil) and (Message.Msg = WM_NCLBUTTONDBLCLK) then if DockClient.CanFloat then DockClient.RestoreChild; Result := inherited DockClientWindowProc(DockClient, Message); end; { TCnDockSplitter } constructor TCnDockSplitter.Create(AOwner: TComponent); begin inherited Create(AOwner); AutoSnap := False; end; function TCnDockSplitter.FindControl: TControl; begin if DockServer <> nil then Result := DockServer.GetDockPanelWithAlign(Align) else Result := inherited FindControl; end; function TCnDockSplitter.GetSplitterIndex: Integer; begin case Align of alTop: Result := 0; alBottom: Result := 1; alLeft: Result := 2; alRight: Result := 3; else Result := -1; end; end; { TCnBasicServerOption } procedure TCnBasicServerOption.Assign(Source: TPersistent); begin if Source is TCnBasicServerOption then begin end; inherited Assign(Source); end; constructor TCnBasicServerOption.Create(ADockStyle: TCnBasicDockStyle); begin FDockStyle := ADockStyle; end; destructor TCnBasicServerOption.Destroy; begin inherited Destroy; end; procedure TCnBasicServerOption.ResetDockClientOption( ADockClient: TCnDockClient); begin // 本来这个函数是一个纯虚函数,但是不知道怎么回事编译要出错,只好改成一般的虚函数 end; procedure TCnBasicServerOption.ResetDockServerOption( ADockServer: TCnDockServer); begin // 本来这个函数是一个纯虚函数,但是不知道怎么回事编译要出错,只好改成一般的虚函数 end; { TCnBasicTabServerOption } procedure TCnBasicTabServerOption.Assign(Source: TPersistent); begin if Source is TCnBasicTabServerOption then begin FTabPosition := TCnBasicTabServerOption(Source).FTabPosition; FHotTrack := TCnBasicTabServerOption(Source).FHotTrack; end; inherited Assign(Source); end; constructor TCnBasicTabServerOption.Create(ADockStyle: TCnBasicDockStyle); begin inherited Create(ADockStyle); FHotTrack := False; FTabPosition := tpTop; end; destructor TCnBasicTabServerOption.Destroy; begin inherited Destroy; end; procedure TCnBasicTabServerOption.ResetDockClientOption( ADockClient: TCnDockClient); var PageControl: TCnTabPageControl; begin if ADockClient = nil then Exit; { 得到这个ADockClient所在的窗体的PageControl } PageControl := TCnTabPageControl(TCnTabDockHostForm(ADockClient.ParentForm).PageControl); ResetTabPageControl(PageControl); if PageControl <> nil then PageControl.Invalidate; end; procedure TCnBasicTabServerOption.ResetDockControlOption; var i: Integer; ADockClient: TCnDockClient; begin { 循环DockStyle的DockBaseControlList列表,然后把每一个 TCnDockClient取出来,然后调用各自的函数重新设置它们的选项 } for i := 0 to DockStyle.DockBaseControlListCount - 1 do begin if DockStyle.DockBaseControlLists[i] is TCnDockClient then begin ADockClient := TCnDockClient(DockStyle.DockBaseControlLists[i]); if ADockClient.ParentForm is TCnTabDockHostForm then begin { 重新设置TCnDockClient的选项 } ResetDockClientOption(ADockClient); end; end; end; end; procedure TCnBasicTabServerOption.ResetDockServerOption( ADockServer: TCnDockServer); begin { do nothing } end; procedure TCnBasicTabServerOption.ResetTabPageControl( APage: TCnTabPageControl); begin if APage = nil then Exit; APage.HotTrack := FHotTrack; APage.TabPosition := FTabPosition; end; procedure TCnBasicTabServerOption.SetHotTrack(const Value: Boolean); begin if FHotTrack <> Value then begin FHotTrack := Value; ResetDockControlOption; end; end; procedure TCnBasicTabServerOption.SetTabPosition( const Value: TTabPosition); begin if FTabPosition <> Value then begin FTabPosition := Value; ResetDockControlOption; end; end; { TCnBasicConjoinServerOption } procedure TCnBasicConjoinServerOption.Assign(Source: TPersistent); begin if Source is TCnBasicConjoinServerOption then begin FGrabbersSize := TCnBasicConjoinServerOption(Source).FGrabbersSize; FSplitterWidth := TCnBasicConjoinServerOption(Source).FSplitterWidth; end; inherited Assign(Source); end; constructor TCnBasicConjoinServerOption.Create( ADockStyle: TCnBasicDockStyle); begin inherited Create(ADockStyle); GrabbersSize := 12; SplitterWidth := 4; end; destructor TCnBasicConjoinServerOption.Destroy; begin inherited Destroy; end; procedure TCnBasicConjoinServerOption.ResetConjoinPanel( APanel: TCnConjoinPanel); begin APanel.CnDockManager.GrabberSize := FGrabbersSize; APanel.CnDockManager.SplitterWidth := FSplitterWidth; end; procedure TCnBasicConjoinServerOption.ResetDockClientOption( ADockClient: TCnDockClient); var ConjoinPanel: TCnConjoinPanel; begin if ADockClient = nil then Exit; { 得到这个ADockClient所在的窗体的Panel } ConjoinPanel := TCnConjoinPanel(TCnConjoinDockHostForm(ADockClient.ParentForm).Panel); ResetConjoinPanel(ConjoinPanel); ConjoinPanel.Invalidate; end; procedure TCnBasicConjoinServerOption.ResetDockControlOption; var i: Integer; ADockServer: TCnDockServer; ADockClient: TCnDockClient; begin if DockStyle = nil then Exit; { 循环DockStyle的DockBaseControlList列表,然后把每一个TCnDockServer或者 TCnDockClient取出来,然后调用各自的函数重新设置它们的选项 } for i := 0 to DockStyle.DockBaseControlListCount - 1 do begin if DockStyle.DockBaseControlLists[i] is TCnDockServer then begin { 重新设置TCnDockServer的选项 } ADockServer := TCnDockServer(DockStyle.DockBaseControlLists[i]); ResetDockServerOption(ADockServer); end else if DockStyle.DockBaseControlLists[i] is TCnDockClient then begin ADockClient := TCnDockClient(DockStyle.DockBaseControlLists[i]); if ADockClient.ParentForm.HostDockSite is TCnConjoinPanel then begin { 重新设置TCnDockClient的选项 } ADockClient := FindDockClient(ADockClient.ParentForm.HostDockSite.Parent); ResetDockClientOption(ADockClient); end; end; end; end; procedure TCnBasicConjoinServerOption.ResetDockPanel(APanel: TCnDockPanel); begin APanel.CnDockManager.GrabberSize := FGrabbersSize; APanel.CnDockManager.SplitterWidth := FSplitterWidth; end; procedure TCnBasicConjoinServerOption.ResetDockServerOption( ADockServer: TCnDockServer); var i: Integer; begin // Panel必须不能为空 if ADockServer = nil then Exit; for i := 0 to 3 do begin if ADockServer.DockPanel[i] = nil then break; ResetDockPanel(ADockServer.DockPanel[i]); ADockServer.DockPanel[i].Invalidate; end; end; procedure TCnBasicConjoinServerOption.SetGrabbersSize( const Value: TGrabbersSize); begin if FGrabbersSize <> Value then begin FGrabbersSize := Value; ResetDockControlOption; end; end; procedure TCnBasicConjoinServerOption.SetGrabbersSize_WithoutChangeSystemInfo( const Value: TGrabbersSize); begin FGrabbersSize := Value; end; procedure TCnBasicConjoinServerOption.SetSplitterWidth( const Value: TSplitterWidth); begin if FSplitterWidth <> Value then begin FSplitterWidth := Value; ResetDockControlOption; end; end; procedure TCnBasicConjoinServerOption.SetSplitterWidth_WithoutChangeSystemInfo( const Value: TSplitterWidth); begin FSplitterWidth := Value; end; { TCnAdvTabPageControl } procedure TCnAdvTabPageControl.CMUnDockClient( var Message: TCMUnDockClient); begin inherited; end; procedure TCnAdvTabPageControl.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, Source.TargetControl); inherited; end; function TCnAdvTabPageControl.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, NewTarget); Result := inherited CustomUnDock(Source, NewTarget, Client) end; destructor TCnAdvTabPageControl.Destroy; var DockClient: TCnDockClient; begin DockClient := FindDockClient(Parent); if (DockClient <> nil) and (DockClient.LastDockSite is TCnDockPanel) then begin with TCnDockPanel(DockClient.LastDockSite) do begin if UseDockManager and (CnDockManager <> nil) then CnDockManager.RemoveControl(Self.Parent); end; end; inherited; end; procedure TCnAdvTabPageControl.DockDrop(Source: TDragDockObject; X, Y: Integer); begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, TControl(Source.DragTarget)); inherited; end; function TCnAdvTabPageControl.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Client, NewTarget); Result := inherited DoUnDock(NewTarget, Client); end; { TCnAdvConjoinPanel } procedure TCnAdvConjoinPanel.CMUnDockClient(var Message: TCMUnDockClient); begin inherited; end; procedure TCnAdvConjoinPanel.CustomDockDrop(Source: TCnDragDockObject; X, Y: Integer); begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, Source.TargetControl); inherited; end; function TCnAdvConjoinPanel.CustomUnDock(Source: TCnDragDockObject; NewTarget: TWinControl; Client: TControl): Boolean; begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, NewTarget); Result := inherited CustomUnDock(Source, NewTarget, Client) end; procedure TCnAdvConjoinPanel.DockDrop(Source: TDragDockObject; X, Y: Integer); begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Source.Control, TControl(Source.DragTarget)); inherited; end; function TCnAdvConjoinPanel.DoUnDock(NewTarget: TWinControl; Client: TControl): Boolean; begin // 设置Client上的TCnDockClient控件的LastDockSite if not IsLoading then ResetDockClient(Client, NewTarget); Result := inherited DoUnDock(NewTarget, Client); end; { Initialization and cleanup } procedure InitDockPresident; var OSVERSIONINFO: TOSVERSIONINFO; begin { 创建一个TCnDockPresident的实例 } if CnGlobalDockPresident <> nil then CnGlobalDockPresident.Free; CnGlobalDockPresident := TCnDockPresident.Create; { 判断当前操作系统的版本, 是WindowsXP还是其他, 以后会用到的 } OSVERSIONINFO.dwOSVersionInfoSize := sizeof(TOSVERSIONINFO); GetVersionEx(OSVERSIONINFO); IsWinXP := (OSVERSIONINFO.dwMajorVersion = 5) and (OSVERSIONINFO.dwMinorVersion = 1); end; procedure DoneDockPresident; begin { 释放这个TCnDockPresident的实例 } if CnGlobalDockPresident <> nil then begin CnGlobalDockPresident.Free; CnGlobalDockPresident := nil; end; end; initialization InitDockPresident; finalization DoneDockPresident; end.