Delphi的可停靠窗体间可以相互停靠,而且花样还很多,可以停靠成并排的,也可以停靠成PageControl样式的,两个可停靠窗体合并后的窗体又可以再和别的可停靠窗体合并,形成树状。下面来介绍这方面的技术。
1、CM_DOCKCLIENT消息和TCMDockClient结构,CM_DOCKCLIENT消息和TCMDockClient结构是相互对应的。
1>、TCMDockClient的结构是:
TCMDockClient = packed record
Msg: Cardinal;
DockSource: TDragDockObject;
MousePos: TSmallPoint;
Result: Integer;
end;
参数说明:
DockSource:包含了停靠—拖动操作的信息,其中有一个重要的属性是Control,另一个重要的属性是DockRect,就是停靠的位置;
MousePos是:鼠标的位置。
2>、CM_DOCKCLIENT事件在停靠和被停靠控件都可以捕获,因为它是TWinControl类发出的。
procedure TWinControl.DockDrop(Source: TDragDockObject; X, Y: Integer);
begin
if (Perform(CM_DOCKCLIENT, Integer(Source), Integer(SmallPoint(X, Y))) >= 0)
and Assigned(FOnDockDrop) then
FOnDockDrop(Self, Source, X, Y);
end;
从Delphi的DockDrop函数定义可以看出,TWinControl是先发送DOCKCLIENT消息,再触发OnDockDrop事件的。
2、用ManualDock函数可以安全的完成停靠功能,不要用Dock函数。
function ManualDock(NewDockSite: TWinControl; DropControl: TControl = nil; ControlSide: TAlign = alNone): Boolean;
参数说明:
NewDockSite:要被停靠的窗体;
DropControl:已经存在于NewDockSite的TControl,在这里可以把它设成nil;
ControlSide: 停靠的位置,可以是上,下,左,右,全部等。
相应的还有一个:ManualFloat和UnDock函数
1.基本步骤:
1>、先创建一个新窗体:
设置属性Name:DockableForm;DockSite:True ;DragKind:dkDock;DragMode:dmAutomatic(自动停靠,表示当鼠标在工具条上点击并移动后,会自动发起拖放动作)。
DockSite必须设置为True,不然不会触发DOCKCLIENT消息处理函数,也就不能停靠了。
DragMode必须设置为dmAutomatic:不然停靠如宿主窗体之后,就不能再拖出来。
2>、再创建一个宿主窗体:
设置属性Name:TiledHost;DockSite:True;UseDockManager:True,表示窗体自动管理停靠。它的作用是用来装载两个DockableForm的。
注意:使用窗体作为停靠锚点时不会出现拖放把手和关闭按钮,而且停靠多个组件时,也不会自动排列,而是随意排列。所以,必须设置UseDockManager为True,使用一个停靠管理器来管理停靠的动作。
3>、在DockableForm中捕获DOCKCLIENT消息,在里面完成两个窗体的相互停靠
在private中声明消息处理函数及它的实现代码:
procedure CMDockClient(var Message: TCMDockClient); message CM_DOCKCLIENT;
end;
procedure TDockableForm.CMDockClient(var Message: TCMDockClient);
var
Host: TForm;
begin
if Message.DockSource.Control is TDockableForm then
begin
Host := TTiledHost.Create(Application);// 创建一个窗体实例
Host.BoundsRect := Self.BoundsRect;// 获取组件所有角的像素位置,设置宿主窗体大小
// 完成窗体的相互停靠
Self.ManualDock(Host, nil, alNone); // 把自己停靠到TTiledHost
Self.DockSite := False;// 不允许其他控件停靠在自己上面
// 把Message.DockSource.Control也停靠到TTiledHost,完成窗体的相互停靠
Message.DockSource.Control.ManualDock(Host, nil, alNone);
TDockableForm(Message.DockSource.Control).DockSite := False;
Host.Visible := True;//必须添加,不然窗体全部消失不见
end;
end;
先解释一下上面的代码,首先创建TTiledHost的实例,然后用ManualDock函数把自己停靠到TTiledHost,把Message.DockSource.Control也停靠到TTiledHost,这样就完成了窗体的相互停靠,
4>、就在DockableForm的OnDockOver事件里加入代码,使程序产生停靠的预览效果。
procedure TDockableForm.FormDockOver(Sender: TObject;
Source: TDragDockObject; X, Y: Integer; State: TDragState;
var Accept: Boolean);
var
ARect: TRect;
begin
Accept := Source.Control is TDockableForm;
if Accept then
begin
ARect.TopLeft := ClientToScreen(Point(0, 0));
ARect.BottomRight := ClientToScreen(
Point(ClientWidth div 2, ClientHeight));
Source.DockRect := ARect;
end;
end;
5>、在DockableForm中添加一个按钮创建新的DockableForm。
Var i: integer;
procedure TDockableForm.btn1Click(Sender: TObject);
var
AForm: TDockableForm;
begin
AForm := TDockableForm.Create(Application);
AForm.Caption := 'AForm' + IntToStr(i);
Inc(i);
AForm.Show;
end;
6>、运行程序
点击按钮生成新的DockableForm时,把其中一个拖动到另一个上,就可以实现两个窗体之间的相互停靠,并一起在HostHost中显示。