Delphi的IDE是本身就是一個非常精彩的軟件,其中涵含了許多非常寶貴的軟件知識。IDE中有一個窗體設計器,控件放在裡面,就可以隨意移動,以及調整大小,如果能夠自己實現一個類似於這樣的窗體設計器,那真是一件非常美妙事情。本文實現的就是窗體設計器中最重要的部分,一個移動控件的類,控件要求從TControl繼承下來,在介紹如何實現之前,先說說這個類的用法:
其中有兩個類:
TDragClass就是實現拉動的類
TDragPoint是控件周圍出現的拉動點的類
用法很簡單:
創建一個TDragClass對象
將要實現拉動的控件傳進去就行了
比如:
myDrag.addControl(Edit1);
這樣Edit1就能實現拉動和移動了。
另外有兩個屬性來控制移動的方式
isMoveStep:boolean
指定移動的方式,True為跳躍式,False為連續式,默認情況下是False,即連續式。
所謂跳躍式,即移動或拉動控件時,控件是以離散的方式在改變自己的位置和大小的,這個對窗體設計器中的控件對齊有幫助。而連續式,當然就是以連續的方式使控件的位置和大小得到改變。
MoveStep :integer
當移動方式為跳躍式時,該屬性指定跳躍的大小,范圍在5-20之間
另外還有一個方法:SetPointVisible(value:Boolean);用於指定移動點的可見性。在Delphi中,當你點擊窗口時,控件周圍的八個小點就消失了,即用此原理。
現在開始進入到具體實現的部分了,當你點擊Delphi的窗體設計器中的控件時,控件周圍出現了八個小點,這八個小點其實也是窗口類:TGrabHandle。預想中要實現控件移動,得有一個標識你正在移動或拉動的東西,這八個小點正是,Delphi的這種做法可以借鑒。於是我實現了一個移動點類:TDragPoint,該的對象將作為TDragClass的成員之一,具體等一下再講。現在來看它的實現,其實非常簡單,因為VCL給了我們一個有自繪能力的類TCustomControl,只要從這裡繼承下來,再重載其中的Paint方法,自己來畫這個移動點就行了。
代碼非常簡單,這裡就不多說了:
//---------TDragPoint--------------------------
unit UDragPoint;
interface
uses Windows, Messages,Controls,Classes,Graphics;
type
TDragPoint=class(TCustomControl)
protected
procedure Paint;override;
public
//處理移動時用變量
isDown:Boolean;
PrevP,NextP:TPoint;
constructor Create(AOwner: TComponent); override;
procedure CreateWnd; override;
published
property OnMouseMove;
property OnMouseDown;
property OnMouseUp;
end;
implementation
{ TDragPoint }
constructor TDragPoint.create;
begin
inherited Create(AOwner);
isDown:=False;
Width:=6;
Height:=6;
end;
procedure TDragPoint.CreateWnd;
begin
inherited;
//使該類位窗口最前
BringWindowToTop(self.Handle);
end;
procedure TDragPoint.Paint;
begin
Canvas.Brush.Color:=clBlack;
Canvas.Brush.Style:=bsSolid;
Canvas.Rectangle(0,0,width,Height);
end;
end.
這裡有必須談到的一點是該類重載了WndCreate,並在其中寫入BringWindowToTop(self.Handle);這樣做目的是讓這些移動點控件能夠位於窗口的最前位置。另外在其中顯化了三個鼠標事件:
property OnMouseMove;
property OnMouseDown;
property OnMouseUp;
目的是為了在TDragClass中實現移動這些點。
現在可以進入主題,來說明TDragClass的實現了。
其中有一個保存傳進來的控件的列表類:FConList:TList;還有一個標識當前正在被移動或拉動的控件在FConList中的索引FCurActiveCon:Integer;
還有控件事件相關的成員
FConMouseDown:TMouseEvent;
FConMouseMove:TMouseMoveEvent;
FConMouseup:TMouseEvent;
這三個事件方法指針指向所有傳進來的控件的鼠標事件的處理函數,在Create中將得到賦值。而所有控件的鼠標處理函數將在類中實現。
接下來就到了最重要的成員了:FPointRec:TPointRec;這是一個記錄類型,其定義為:
TPointRec=record
LeftTop:TDragPoint;
LeftBottom:TDragPoint;
RightTop:TDragPoint;
RightButton:TDragPoint;
LeftMid:TDragPoint;
TopMid:TDragPoint;
RightMid:TDragPoint;
ButtonMid:TDragPoint;
end;
這正是當前被移動控件邊緣的八個點。這八個點會粘在被移動控件的邊緣。
上面說過該類可以實現跳躍式移動或拉動則必定有相關的成員: FisMoveStep:Boolean;
FMoveStep:integer;
MoveX,MoveY:integer;
FisMoveStep指定是否為跳躍式,FMoveStep為跳躍的幅度,MoveX,MoveY標識控件移動或拉動的距離是否達到了FMoveStep,是就改變控件位置和大小,如此重復
除了上面那些成員,類中還定義了一些相類的方法,大概如下:
//-------對移動點類的操作—
//創建移動點類
procedure CreateDragPoint(PointParent:TWinControl);
//設定移動點類的位置
procedure SetPointPos(posRect:TRect);
//指定移動點類的父窗口
procedure SetPointParent(PointParent:TWinControl);
//設置移動點類的鼠標事件
procedure SetPointEvent;
//設置移動點類的可見性
procedure SetPointVisible(Visibled:Boolean);
//三個控件事件處理函數,所有控件的鼠標處理函數都將是這個,主要是解決控件的移動
//以及移動點類的位置,當你點擊某一個控件的時候,移動點類會附著到這個控件的邊緣
procedure ConMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ConMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure ConMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
//移動點類的鼠標處理事件,解決移動點類的移動,以及當前控件的大小改變
procedure PointMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure PointMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure PointMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
最後一個重要方法是function addControl(AddCon:Pointer):Boolean;
控件從這裡加入,就可以實現移動和拉動了。
下面就將類實現比較重要的幾點略說一下吧(主要還是看代碼吧)
在類的構造函數中,將上面的三個控件處理函數指定給三個指針成員:
FConMouseDown:=ConMouseDown;
FConMouseMove:=ConMouseMove;
FConMouseup:=ConMouseUp;
現在這三個成員就指定了三個處理函數的地址了,等一下就可以看到那些控件的鼠標消息是怎麼和這三個處理函數聯系在一起的,實現就在AddControl函數中。
AddControl是一個非常重要的方法,在控件加入之前,它要先判斷控件是否有Parent值,沒有則不能加入,更重要的一點是,在FConList是否已經有這個控件了,即該控件已經加入過了,如果已經加入了,則不能再加一次,代碼如下:
//如果該控件已經在列表中了,則加入失敗
for i:=0 to FConList.Count-1 do
if Integer(AddCon)=Integer(FConList.Items[i]) then
begin
result:=false;
exit;
end;
如果可以加入則先加入列表類中,再指定當前活動控件:
FConList.Add(AddCon);
FCurActiveCon:=FConList.Count-1;
而AddControl中還有一個比較重要的TempCon.Parent.DoubleBuffered:=True;
即加入的控件的父窗口設為雙緩沖模式,這樣在移動控件或拉動控件大小的時候,不會出現閃爍現象。
接著就是為加入的控件指定鼠標處理函數了,但加入的是TControl,而他的鼠標事件指針被設為保護類型,因此無法獲得,但他的子類把他們顯化出來了。這裡用了一種折衷的方案:
TButton(TempCon).OnMouseDown:=FconMouseDown;
TButton(TempCon).OnMouseMove:=FconMouseMove;
TButton(TempCon).OnMouseUp:=FconMouseUp;
這樣做並不會出錯,但顯得怪怪的,但不理他了,能實現功能就行了。現在加入控件的鼠標事件都將會在這裡的三個處理函數中處理了。
最後,將移動點類移動該控件的邊緣去。
說得夠雜的,各位可以和第二部分的原代碼對照著看,這樣會更好一些。
再稍微講一下跳躍式移動或拉動控件的實現,FMoveStep指定跳躍的幅度,MoveX,MoveY:integer;用在移動點類和控件的鼠標事件中,累加鼠標移動的距離,當達到FMoveStep時,就移動控件,或改變控件的大小,然後將MoveX,MoveY變為0,又繼續累加,如此循環
至於其他的就沒有什麼好說的了,各位還是看看源代碼吧,也並不是很難理解。代碼在第二部分給出。