在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一、基础知识介绍1、VCL组件的基础知识 在TWinControl类中有一个DockSite属性(boolean),它的作用是是否允许别的控件停靠在它的上面;在TControl类中有一个DragKind属性,如果要这个控件能停靠在别的控件上,就把DragKind属性设成dkDock。就这么简单,只要设置一下属性,一个支持停靠的程序就完成了。当然,上面说的只是最最基本的步骤,有了以上两步,我们就可以继续编写代码实现更复杂的功能。
2、停靠操作常用的属性 1>.TControl. TBDockHeight // 停靠控件在停靠时的的高度; 2>.TControl. LRDockWidth // 停靠控件在停靠时的的宽度; 3>.TControl. UnDockHeight // 停靠控件在浮动时的的高度; 4>.TControl. UnDockWidth // 停靠控件在浮动时的的宽度; 5>.TControl. HostDockSite // 被停靠控件的实例; 6>.TControl. FloatingDockSiteClass // 为停靠控件在浮动时创建容器(自动装载); 7>.TControl. Floating // 是否浮动 9>.TControl. DockOrientation // 停靠控件的方位 10>.TWinControl .DockClientCount // 被停靠控件里面有几个已经停靠的控件 11>.TWinControl . DockClients // 被停靠控件里面有已经停靠的控件的列表 12>.TWinControl . DockManager // 一个控制停靠的类,其实是一个ActiveX控件,和它对应的类是TDockTree. 13>. TWinControl .UseDockManager // 是否使用DockManager。 3、停靠操作常用的消息: 1>.CM_DOCKCLIENT // 在DockDrop方式前发生此消息(被停靠对象的事件?) 2>.CM_DOCKNOTIFICATION, 3>.CM_UNDOCKCLIENT 如果读者想对停靠技术有更深入的了解,可以看Delphi自带的例子,路径是Delphi5DemoDocking.
4、停靠时Delphi会产生的方法和事件 1>、OnDockOver:在停靠控件掠过被停靠控件时触发的。主要作用是控制停靠控件的预览位置,出现虚线包含的范围(Source.DockRect)(是被停靠控件的事件)。 OnDockOver(Sender: TObject; Source: TDragDockObject;X, Y: Integer; State: TDragState; var Accept: Boolean); 参数说明: Source:包含了停靠—拖动操作的信息,其中有一个重要的属性是Control,就是停靠控件,另一个重要的属性是DockRect,就是停靠的位置; X,Y:是鼠标的位置, State:的状态有dsDragEnter, dsDragLeave, dsDragMove,分别表示拖动进入,拖动离开,拖动移动; Accept:是是否同意停靠的意思。
2>、OnDockDrop,在停靠控件进入被停靠控件时发生的,作用是控制停靠控件的最终位置。它才是决定停靠控件在哪里出现的罪魁祸首(是被停靠控件的事件)。 OnDockDrop(Sender: TObject; Source: TDragDockObject;X, Y: Integer); 参数说明: 和OnDockOver差不多,只是少了State: TDragState和var Accept: Boolean。
3>、OnGetSiteInfo当停靠控件移动时触发的,所以经常触发(是被停靠控件的事件)。 OnGetSiteInfo(Sender: TObject; DockClient: TControl; var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean); 参数说明: DockClient:就是停靠控件。 CanDock:和OnDockOver中的Accept差不多,都是询问是否允许停靠。当为False时,DragOver事件不会执行。 (在这里可以不写,CanDock默认就是True,也可以写上CanDock := DockClient is TDockableForm;)
4>、OnStartDock:停靠开始时触发(是停靠控件的事件)。 OnStartDock(Sender: TObject; var DragObject: TDragDockObject);// 经常会被触发
5>、OnEndDock:停靠结尾时触发(是停靠控件的事件)。 OnEndDock(Sender, Target: TObject; X, Y: Integer);// 经常会被触发
6>、OnUnDock:不停靠(也就是被拖出来变成浮动时触发)(是停靠控件的事件?个人觉得是被停靠对象的事件)。 OnUnDock(Sender: TObject; Client: TControl; NewTarget: TWinControl; var Allow: Boolean);//停靠窗体变成浮动时触发
二、在一个窗体中停靠另一个窗体的实例一般的支持停靠的程序都可以在主窗口的上下左右停靠,也就是说在主窗口的边上放上能被停靠的控件比较好(只要是从TWinControl继承的都行),一般我们都选择TPanel,为了便于读者理解,我们可以假定主窗口的左边可以停靠。
1、基本步骤: 1>、在主窗口上放一个Panel,称为被停靠控件: 设置属性Name:LeftDockPanel;Align:alLeft; width:0,DockSite:True; 2>、然后再放一个TSplitter,可以调节LeftDockPanel的大小: 设置属性Name:LeftSplitter;Align:alLeft。 3>、再建一个窗体TForm,用于停靠,称为停靠控件。 设置属性: Name:DockableForm;DragKind:dkDock;DragMode:dmAutomatic(自动停靠)。 现在我们可以运行这个程序了(鼠标要进入Panel区域才能停靠)。停靠的窗体停靠进去后就不见了!这是可以使用停靠时Delphi会产生一些事件,控制它的显示情况。
2、控制它的显示,需要添加以下事件: 1>、OnDockOver:控制停靠窗体的预览位置 procedure TMainForm.LeftDockPanelDockOver(Sender: TObject; Source: TDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var ARect: TRect; begin Accept := Source.Control is TDockableForm; if Accept then begin //修改预览停靠位置 ARect.TopLeft := LeftDockPanel.ClientToScreen(Point(0, 0)); ARect.BottomRight := LeftDockPanel.ClientToScreen( Point(Self.ClientWidth div 3, LeftDockPanel.Height)); Source.DockRect := ARect; end; end; 现在再运行程序,当你把DockableForm拖动到主窗口左边时,已经出现了预览停靠位置,也就是虚线包含的范围。
2>、OnDockDrop:控制停靠窗体的最终位置 procedure TMainForm.LeftDockPanelDockDrop(Sender: TObject; Source: TDragDockObject; X, Y: Integer); Begin LeftDockPanel.Width := ClientWidth div 3; LeftSplitter.Left := LeftDockPanel.Width + LeftSplitter.Width; End; 现在再运行程序,出现了一个和Delphi的IDE完全一样的停靠窗体,上面是两条横线,用来把它拖出来,右上角有一个小X是用来关闭的。
3>、onClose:停靠窗体的关闭事件中控制侧边栏的关闭 点击右上角那个小X关闭DockableForm时,它会触发DockableForm的OnClose事件,在OnClose事件中把LeftDockPanel的宽度设为0就行了。 procedure TDockableForm.FormClose(Sender: TObject; var Action: TCloseAction); begin MainForm.LeftDockPanel.Width := 0; Action := caHide; end; 以上所讲的是如何在主窗口上停靠窗体,原代码都通过测试。同理,我们可以在主窗口的右边,下边,上边都实现停靠功能。
三、总结:在Delphi中只要是从TWinControl继承的控件都支持被停靠(如上面的LeftDockPanel),也就是有DockSite这个属性;所有从TControl继承的控件都支持停靠(如上面的DockableForm),也就是有DragKind这个属性.所以支持被停靠的控件都支持停靠,支持停靠的控件不一定支持被停靠,道理很简单,因为TWinControl继承于TControl。OnDockOver事件是控制停靠窗体的预览位置;OnDockDrap事件是控制停靠窗体的最终位置;OnGetSiteInfo是询问是否可以停靠;OnStartDock是停靠开始,OnEndDock是停靠结尾,OnUnDock是不停靠(也就是被拖出来时)。
一、基础知识介绍1、CM_DOCKCLIENT消息和TCMDockClient结构,CM_DOCKCLIENT消息和TCMDockClient结构是相互对应的。 1>、TCMDockClient的结构是: TCMDockClient = packed record Msg: Cardinal; DockSource: TDragDockObject; MousePos: TSmallPoint; Result: Integer; end; 参数说明: DockSource:包含了停靠—拖动操作的信息,其中有一个重要的属性是Control,另一个重要的属性是DockRect,就是停靠的位置; MousePos是:鼠标的位置。
2>、CM_DOCKCLIENT事件在停靠和被停靠控件都可以捕获,因为它是TWinControl类发出的。 procedure TWinControl.DockDrop(Source: TDragDockObject; X, Y: Integer); begin if (Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0) and Assigned(FOnDockDrop) then FOnDockDrop(Self, Source, X, Y); end; 从Delphi的DockDrop函数定义可以看出,TWinControl是先发送DOCKCLIENT消息,再触发OnDockDrop事件的。
2、用ManualDock函数可以安全的完成停靠功能,不要用Dock函数。 function ManualDock(NewDockSite: TWinControl; DropControl: TControl = nil; ControlSide: TAlign = alNone): Boolean; 参数说明: NewDockSite:要被停靠的窗体; DropControl:已经存在于NewDockSite的TControl,在这里可以把它设成nil; ControlSide: 停靠的位置,可以是上,下,左,右,全部等。 相应的还有一个:ManualFloat和UnDock函数
二、窗体之间相互停靠的实例1.基本步骤: 1>、先创建一个新窗体: 设置属性Name:DockableForm;DockSite:True ;DragKind:dkDock;DragMode:dmAutomatic(自动停靠,表示当鼠标在工具条上点击并移动后,会自动发起拖放动作)。 DockSite必须设置为True,不然不会触发DOCKCLIENT消息处理函数,也就不能停靠了。 DragMode必须设置为dmAutomatic:不然停靠如宿主窗体之后,就不能再拖出来。
2>、再创建一个宿主窗体: 设置属性Name:TiledHost;DockSite:True;UseDockManager:True,表示窗体自动管理停靠。它的作用是用来装载两个DockableForm的。 注意:使用窗体作为停靠锚点时不会出现拖放把手和关闭按钮,而且停靠多个组件时,也不会自动排列,而是随意排列。所以,必须设置UseDockManager为True,使用一个停靠管理器来管理停靠的动作。
3>、在DockableForm中捕获DOCKCLIENT消息,在里面完成两个窗体的相互停靠 在private中声明消息处理函数及它的实现代码:
procedure CMDockClient(var Message: TCMDockClient); message CM_DOCKCLIENT; end; procedure TDockableForm.CMDockClient(var Message: TCMDockClient); var Host: TForm; begin if Message.DockSource.Control is TDockableForm then begin Host := TTiledHost.Create(Application);// 创建一个窗体实例 Host.BoundsRect := Self.BoundsRect;// 获取组件所有角的像素位置,设置宿主窗体大小 // 完成窗体的相互停靠 Self.ManualDock(Host, nil, alNone); // 把自己停靠到TTiledHost Self.DockSite := False;// 不允许其他控件停靠在自己上面 // 把Message.DockSource.Control也停靠到TTiledHost,完成窗体的相互停靠 Message.DockSource.Control.ManualDock(Host, nil, alNone); TDockableForm(Message.DockSource.Control).DockSite := False; Host.Visible := True;//必须添加,不然窗体全部消失不见 end; end;
4>、就在DockableForm的OnDockOver事件里加入代码,使程序产生停靠的预览效果。 procedure TDockableForm.FormDockOver(Sender: TObject; Source: TDragDockObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var ARect: TRect; begin Accept := Source.Control is TDockableForm; if Accept then begin ARect.TopLeft := ClientToScreen(Point(0, 0)); ARect.BottomRight := ClientToScreen( Point(ClientWidth div 2, ClientHeight)); Source.DockRect := ARect; end; end; 5>、在DockableForm中添加一个按钮创建新的DockableForm。
Var i: integer; procedure TDockableForm.btn1Click(Sender: TObject); var AForm: TDockableForm; begin AForm := TDockableForm.Create(Application); AForm.Caption := 'AForm' + IntToStr(i); Inc(i); AForm.Show; end; 点击按钮生成新的DockableForm时,把其中一个拖动到另一个上,就可以实现两个窗体之间的相互停靠,并一起在HostHost中显示。
因为TPageControl组件重载了TWinControl组件的DoAddDockClient和DoRemoveDockClient方法,能过自动响应停靠动作添加新的也没,而当浮动被停靠的窗口后将自动的将先前创建的TTabSheet也没删除,我们不需要写代码,只要设定基本的属性就可以实现分页停靠的功能。
一、两个窗体停靠成PageControl样式的实例1.基本步骤: 1>、先创建一个新窗体(DockableForm): 设置属性Name:DockableForm;DockSite:True ;DragKind:dkDock;DragMode:dmAutomatic(自动停靠,表示当鼠标在工具条上点击并移动后,会自动发起拖放动作)。 DockSite必须设置为True,不然不会触发DOCKCLIENT消息处理函数,也就不能停靠了。
2023-10-27 2022-08-15 2022-08-17 2022-09-23 2022-08-13 |
请发表评论