Delphi中停靠技術的實現 (轉)
中停靠技術的實現:namespace prefix = o ns = "urn:schemas--com::office" />
隨著技術的不斷進步,軟體介面也越來越美觀,操作也越來越方便。
綜觀市面上比較專業的各種軟體,我們會發現大部分都提供窗體停靠的功能,特別象工具軟體,基本上都或多或少有停靠功能。
自然,Delphi也支援停靠,而且她和VCL緊密結合,對於廣大的Delphi員來說更是一大福音。讓我們省去枯燥的編碼時間。把注意力集中在核心程式的構思上。
先讓我們來複習一下VCL的結構,在TWinControl類中有一個DockSite屬性(boolean),它的作用是是否允許別的停靠在它的上面,在TControl類中有一個DragKind屬性,如果要這個控制元件能停靠在別的控制元件上,就把DragKind屬性設成dkDock。就這麼簡單,只要設定一下屬性,一個支援停靠的程式就完成了。
當然,上面說的只是最最基本的步驟,有了以上兩步,我們就可以繼續編寫程式碼實現更復雜的功能。
一般的支援停靠的程式都可以在主視窗的上下左右停靠,也就是說在主視窗的邊上放上能被停靠的控制元件比較好(只要是從TWinControl繼承的都行),一般我們都選擇TPanel,為了便於讀者理解,我們可以假定主視窗的左邊可以停靠,所以在主視窗上放一個Align屬性為alLeft的Panel,取名為LeftDockPanel,寬度為0,DockSite屬性為True,當然我們的LeftDockPanel應該是可以改變大小的,所以在它右邊再放一個TSplitter,取名為LeftSplitter,Align屬性為alLeft。接下來就是停靠控制元件了,一般的程式停靠控制元件都是窗體,所以我們也建一個窗體,取名叫DockableForm,DragKind屬性設成dkDock,DragMode屬性設為dmAutomatic(自動停靠)。
現在我們可以執行這個程式了,什麼?效果不好?停靠的窗體停靠停靠進去後就不見了!
哦,我差點忘了,當停靠窗體停靠時Delphi會產生一些事件,他們分別是
1.OnDockOver(Sender: T; : TDragDockObject;
X, Y: Integer; State: TDragState; var Accept: Boolean);
2.OnDockDrop(Sender: TObject; Source: TDragDockObject;
X, Y: Integer);
3.OnGetSiteInfo(Sender: TObject; DockClient: TControl;
var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
4.OnStartDock(Sender: TObject;
var DragObject: TDragDockObject);
5.OnEndDock(Sender, Target: TObject; X, Y: Integer);
6.OnUnDock(Sender: TObject; Client: TControl;
NewTarget: TWinControl; var Allow: Boolean);
哇,這麼多,別急,讓我細細道來:
先讓我們來看看第一個事件
OnDockOver是在停靠控制元件(DockableForm)掠過被停靠控制元件(LeftDockPanel)時觸發的。Source包含了停靠—拖動操作的資訊,其中有一個重要的屬性是Control,就是DockableForm,另一個重要的屬性是DockRect,就是停靠的位置;X,Y是滑鼠的位置,State的狀態有dsDragEnter, dsDragLeave, dsDragMove,分別表示拖動進入,拖動離開,拖動移動;Accept是是否同意停靠的意思。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拖動到主視窗左邊時,已經出現了預覽停靠位置,也就是虛線包含的範圍。
怎麼?窗體又不見了?那當然了,我們只是講了OnDockOver,還沒詳細講解OnDockDrop呢,它才是決定停靠窗體在哪裡出現的罪魁禍首:
OnDockDrop(Sender: TObject;
Source: TDragDockObject; X, Y: Integer);
引數和OnDockOver差不多,只是少了State: TDragState和var Accept: Boolean
它是在停靠窗體進入被停靠控制元件時發生的,作用是控制停靠窗體的最終位置。下面新增如下程式碼:
procedure TMainForm.LeftDockPanelDockDrop(Sender: TObject;
Source: TDragDockObject; X, Y: Integer);
Begin
LeftDockPanel.Width := ClientWidth div 3;
LeftSplitter.Left := LeftDockPanel.Width + LeftSplitter.Width;
End;
現在再執行程式,哇塞,成功了。出現了一個和Delphi的完全一樣的停靠窗體,上面是兩條橫線,用來把它拖出來,右上角有一個小X是用來關閉的。
不過好景不長,當我們把它關閉時,裝載DockableForm的LeftDockPanel不能還原,還是霸佔著主視窗的客戶區,怎麼辦?
嘻嘻,忘了告訴你們了,其實Delphi早就為我們作好了一切。
請開啟DockableForm的關閉事件,你會發現原來當你點選右上角那個小X關閉DockableForm時,它會觸發DockableForm的OnClose事件,在OnClose事件中把LeftDockPanel的寬度設為0就行了。
procedure TDockableForm.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
MainForm.LeftDockPanel.Width := 0;
Action := caHide;
end;
以上所講的是如何在主視窗上停靠窗體,原始碼都透過測試。同理,我們可以在主視窗的右邊,下邊,上邊都實現停靠功能。
對了,剛才我們只介紹了OnDockOver和OnDockDrop,忘了介紹別的事件,下面簡單介紹一下:
3.OnGetSiteInfo(Sender: TObject; DockClient: TControl;
var InfluenceRect: TRect; MousePos: TPoint; var CanDock: Boolean);
這個事件是在窗體移動時觸發的,所以經常觸發,它裡面的DockClient就是TDockableForm,
有一個引用引數叫CanDock,和OnDockOver中的Accept差不多,都是詢問是否允許停靠。在這裡可以不寫,CanDock預設就是True,也可以寫上CanDock := DockClient is TDockableForm;
4.OnStartDock(Sender: TObject;
var DragObject: TDragDockObject);
5.OnEndDock(Sender, Target: TObject; X, Y: Integer);
6.OnUnDock(Sender: TObject; Client: TControl;
NewTarget: TWinControl; var Allow: Boolean);
這三個事件都是在DockableForm上面有用,意思分別是停靠開始,停靠結尾,不停靠(也就是被拖出來時)。
OnStartDock和OnEndDock經常會被觸發,
OnUnDock只在停靠窗體變成浮動時觸發
講了那麼多,大家有沒有被搞糊塗?那好,我來做一下總結:
在Delphi中只要是從TWinControl繼承的控制元件都支援被停靠(如上面的LeftDockPanel),也就是有DockSite這個屬性;所有從TControl繼承的控制元件都支援停靠(如上面的DockableForm),也就是有DragKind這個屬性.所以支援被停靠的控制元件都支援停靠,支援停靠的控制元件不一定支援被停靠,道理很簡單,因為TWinControl繼承於TControl。OnDockOver事件是控制停靠窗體的預覽位置;OnDockDrap事件是控制停靠窗體的最終位置;OnGetSiteInfo是詢問是否可以停靠;OnStartDock是停靠開始,OnEndDock是停靠結尾,OnUnDock是不停靠(也就是被拖出來時)。
想必Delphi用的熟的大蝦都知道在Delphi的可停靠窗體間可以相互停靠,而且花樣還很多,可以停靠成並排的,也可以停靠成PageControl樣式的,兩個可停靠窗體合併後的窗體又可以再和別的可停靠窗體合併,形成樹狀。下面來介紹這方面的技術:
說道這裡,我們不得不介紹一下CM_DOCKCLIENT訊息和TCMDockClient結構,
CM_DOCKCLIENT訊息和TCMDockClient結構是相互對應的,TCMDockClient的結構是:
TCMDockClient = packed record
Msg: Cardinal;
DockSource: TDragDockObject;
MousePos: TSmallPoint;
Result: Integer;
end;
其中DockSource包含了停靠—拖動操作的資訊,前面已經提到過;MousePos是滑鼠的位置。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;
可以看出,TWinControl是先傳送DOCKCLIENT訊息,再觸發OnDockDrop事件的。
為了演示可停靠窗體之間相互停靠,我們先建立一個宿主窗體,取名叫TiledHost,把它的DockSite設成True。它的作用是用來裝載兩個DockableForm的。
首先在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 TDockableFothen
begin
Host := TTiledHost.Create(Application);
Host.BoundsRect := Self.BoundsRect;
Self.ManualDock(Host, nil, alNone);
Self.DockSite := False;
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,這樣就完成了窗體的相互停靠,當然,要是我們要程式產生停靠的預覽效果,就在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;
怎麼樣,效果還可以吧。對了,需要注意的是,用ManualDock函式可以的完成停靠功能,不要用Dock函式。ManualDock函式有一些引數:
function ManualDock(NewDockSite: TWinControl; DropControl: TControl = nil; ControlSide: TAlign = alNone): Boolean;
NewDockSite:要被停靠的窗體;
DropControl:已經存在於NewDockSite的TControl,在這裡可以把它設成nil;
ControlSide: 停靠的位置,可以是上,下,左,右,全部等。
當然,我們也可以讓TiledHost也具有和LeftDockPanel一樣有被停靠的功能,只要把TiledHost看成前面的LeftDockPanel,新增一些屬性和事件;把TiledHost看成DockableForm,
就可以有停靠的功能了。具體的做法這裡不再闡述了,相信對VCL有深刻研究的大蝦都知道怎麼做了。
下面我來講一下兩個窗體怎樣停靠成PageControl樣式。
首先建立一個窗體,叫TabHost,在它上面放一個PageControl,Align屬性設成alClient,讓它佔滿整個TabHost,別忘了把PageControl的DockSite屬性設成True.
然後我們依次加入程式碼:
procedure TDockableForm.FormDockOver(Sender: TObject;
Source: TDragDockObject; X, Y: Integer; State: TDragState;
var Accept: Boolean);
var
ARect: TRect;
begin
Accept := Source.Control is TDockableForm;
if Accept then
begin
ARect.TopLeft := ClientToScreen(ClientRect.TopLeft);
ARect.BottomRight := ClientToScreen(ClientRect.BottomRight);
Source.DockRect := ARect;
end;
和
procedure TDockableForm.CMDockClient(var Message: TCMDockClient);
var
Host: TForm;
begin
if Message.DockSource.Control is TDockableForm then
begin
Host := TTabHost.Create(Application);
Host.BoundsRect := Self.BoundsRect;
Self.ManualDock(TTabHost(Host).PageControl1, nil, alClient);
Message.DockSource.Control.ManualDock(TTabHost(Host).PageControl1, nil, alClient);
Host.Visible := True;
End;
End;
程式碼的具體意思在這裡就不再解釋了,同理也可以讓TabHost具有停靠和被停靠的功能。還需要說明一下,TPageControl封裝了一些對停靠的支援,它捕獲了CM_DOCKCLIENT,
CM_DOCKNOTIFICATION,CM_UNDOCKCLIENT,WM_LBUTTONLCLK訊息處理停靠動作。具體可以檢視TPageControl的原始碼。
工具條的停靠也一樣,在主窗體上放一個ControlBar或CoolBar,把他們的DockSite設成True;再在上面放ToolBar, ToolBar的DragKind屬性設成dkDock,DragMode屬性設為dmAutomatic。在這裡,TControl有一個屬性叫FloatingDockSiteClass,它的型別是TWinControl的引用(class of TWinControl),只要在主視窗建立時,把ToolBar的FloatingDockSiteClass屬性設成某一個窗體A,比如在設計時A這個窗體叫ToolBarDockForm,但在程式裡面不用顯式的建立A,Delphi會自動建立,當ToolBar被拖動出來時,Delphi自動把它裝載到ToolBarDockForm裡,當然ToolBarDockForm也要象上面提到的DockableForm一樣設定一定的屬性和新增一些程式碼。
講了一大堆,還是沒有把Delphi支援的停靠功能全部講完,據我所知,還有很多。還是把它們列出來供大家參考(前面介紹的就省略了)
屬性:
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 //一個控制停靠的類,其實是一個控制元件,和它對應的類是TDockTree.
13. TWinControl .UseDockManager //是否使用DockManager。
方法:
1.TControl.ManualFloat //和ManualDock相對應,使浮動。
2.TControl.ReplaceDockedControl //替換停靠控制元件
3.TWinControl .DoAddDockClient
4.TWinControl .DockDrop
5.TWinControl .DockOver
6.TWinControl .DoDockOver
7.TWinControl .DoUnDock
訊息:
CM_DOCKCLIENT,
CM_DOCKNOTIFICATION,
CM_UNDOCKCLIENT
如果讀者想對停靠技術有更深入的瞭解,可以看Delphi自帶的例子,路徑是Delphi5DemoDocking.
本人是菜鳥一個,上面所講的內容,錯誤再所難免,希望大蝦們不要。
如有疏漏,還請各位指教,謝謝。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990881/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- delphi中的時間操作技術(1) (轉)
- delphi中的時間操作技術(2) (轉)
- 在Delphi中實現圖片的旋轉、縮放 (轉)
- 一種新穎的技術:Delphi for DOS!!! (轉)
- 在Delphi中實現任意形狀的窗體 (轉)
- Delphi系列談之:Delphi中的靜態屬性及靜態方法的實現 (轉)
- Delphi中序列通訊的實現 作者:阮幫秋 (轉)
- Apache中URLRewrite技術的實現Apache
- Singleton模式之Delphi實現 (轉)模式
- 我對C++中THUNK一種實現技術的分析 (轉)C++
- 【轉載】SAP 系統中STO+VC 技術實現
- 用Delphi在區域網中實現網上影院 (轉)
- 實現控制元件的移動、改變大小(DELPHI實現) (轉)控制元件
- Delphi中TFlowPanel實現滾動條效果
- Delphi中的類和物件 (轉)物件
- 從現場實施到技術支援的轉折
- Javascript技術實現真正的網上試聽 (轉)JavaScript
- 淺談MFC中超類化技術的實現 (轉)
- IPsec_VPN實現技術【轉載】
- 網上投票作弊的技術實現(純技術交流,勿用作他途!!) (轉)
- 用Delphi實現遠端螢幕抓取 (轉)
- 設計模式、用Delphi實現---->Builder模式 (轉)設計模式UI
- 兩種Delphi實現Singleton模式方法 (轉)模式
- Delphi 技術的優缺點與應用
- 在 Python 中實現 COMET 技術Python
- 滑鼠螢幕取詞技術的原理和實現 (轉)
- SVG中的動畫技術(2) (轉)SVG動畫
- SVG中的動畫技術(1) (轉)SVG動畫
- SVG中的動畫技術(3) (轉)SVG動畫
- linux防火牆實現技術比較(轉)Linux防火牆
- MFC抽象類之動態實現技術 (轉)抽象
- Delphi程式設計:完全控制桌面的實現 (轉)程式設計
- 設計模式、用Delphi實現---->Singleton 模式 (轉)設計模式
- Dll中匯出類--Delphi實戰之一 (轉)
- WMI Series 1:實現Windows管理的各種技術概述 (轉)Windows
- 技術週刊的轉變:如何平衡熱愛與現實?
- Delphi7下仿Ado.Net類的實現 (轉)
- 程式設計技術點滴一(Delphi)程式設計