控件拖动位置的问题 ( 积分: 100 )

赛特

Unregistered / Unconfirmed
GUEST, unregistred user!
我现在实现控件拖动位置是在onmousedown和onmouseup里面写代码实现的,也可以实现控件位置的改变,但我想要的效果是在拖动时,像拖动QQ窗口样有个虚线框。这样可以使人更直观些。该怎样实现呀?谢谢
做个例子发给我嘛,就一个form里面一个panel,然后实现拖动panel的位置
 
我现在实现控件拖动位置是在onmousedown和onmouseup里面写代码实现的,也可以实现控件位置的改变,但我想要的效果是在拖动时,像拖动QQ窗口样有个虚线框。这样可以使人更直观些。该怎样实现呀?谢谢
做个例子发给我嘛,就一个form里面一个panel,然后实现拖动panel的位置
 
用DragDrop和DragOver事件!我在treeview中写过
 
to lmk:
能不能做个例子发给我嘛,就一个form里面一个panel,然后实现拖动panel的位置
谢谢
 
你做一个带虚线筐的cursor资源,拖动的时候screen.cursor := 带虚线筐的cursor
拖动结束screen.cursor := crDefault
 
to ak_2004:
这种方法倒也可以,但是总觉得不是很好,带有欺骗性,呵呵[:D]
 
偶也是菜鸟!这个写不出来,给你找个牛人的例子看看吧!
unit Resizer;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;

const
GRIDDEFAULT = 4;

type
TResizer = class;
TMover = class;

TMovingEvent = procedure(Sender: TResizer; var NewLeft, NewTop: integer) of object;
TSizingEvent = procedure(Sender: TResizer; var NewLeft, NewTop, NewWidth, NewHeight: integer) of object;

TResizer = class(TComponent)
protected
FActive : boolean;
FControl : TControl;
Sizers : TList;
GroupMovers : TList;
FGroup : TWinControl;
FGridX : integer;
FGridY : integer;
FOnSized : TNotifyEvent;
FOnSizing : TSizingEvent;
FOnMoved : TNotifyEvent;
FOnMoving : TMovingEvent;
Sizing : boolean;
Moving : boolean;
OrigSize : TRect;
NewSize : TRect;
DownX : integer;
DownY : integer;
FAllowSize : boolean;
FAllowMove : boolean;
FKeepIn : boolean;
FHotTrack : boolean;
OneMover : TMover;
CurMover : TMover;
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
procedure SetActive(b: boolean);
procedure SetControl(c: TControl);
procedure SetGroup(p: TWinControl);
procedure CreateSizers;
procedure CheckSizers;
procedure ShowSizers;
procedure HideSizers;
procedure SizerDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure SizerUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure SizerMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure MoverDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MoverUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MoverMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure DrawSizeRect(Rect: TRect);
procedure Calc_Size_Rect(SizerNum, dx, dy: integer);
procedure DoSizingEvent;
procedure Calc_Move_Rect(dx, dy: integer);
procedure DoMovingEvent;
procedure Constrain_Size;
procedure Constrain_Move;
procedure MoverKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure DoSizeMove(var Key: Word; Shift: TShiftState; dx, dy: integer);
procedure CreateGroupMovers;
procedure CreateOneMover(m: TMover; c: TControl);
function FindMoverByBuddy(c: TControl): TMover;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Active: boolean read FActive write SetActive default True;
property ResizeControl: TControl read FControl write SetControl;
property ResizeGroup: TWinControl read FGroup write SetGroup;
property GridX: integer read FGridX write FGridX default GRIDDEFAULT;
property GridY: integer read FGridY write FGridY default GRIDDEFAULT;
property OnSized: TNotifyEvent read FOnSized write FOnSized;
property OnSizing: TSizingEvent read FOnSizing write FOnSizing;
property OnMoved: TNotifyEvent read FOnMoved write FOnMoved;
property OnMoving: TMovingEvent read FOnMoving write FOnMoving;
property AllowSize: boolean read FAllowSize write FAllowSize default True;
property AllowMove: boolean read FAllowMove write FAllowMove default True;
property KeepInParent: boolean read FKeepIn write FKeepIn default True;
property HotTrack: boolean read FHotTrack write FHotTrack;
end;

TInvisWin = class(TPanel) // This could also derive from TPanel
protected
procedure WndProc(var Message: TMessage); override;
procedure CreateParams(var Params: TCreateParams); override;
procedure WMDLGCode(var Message: TMessage); message WM_GETDLGCODE;
public
property OnKeyDown;
end;

TMover = class(TInvisWin)
public
Buddy : TControl;
procedure Show;
end;


procedure Register;

implementation

const
SIZE = 6;
HALFSIZE = SIZE div 2;

type
TSizer = class(TPanel)
end;

procedure Register;
begin
RegisterComponents('Samples', [TResizer]);
end;


// *****************************************************************
// TInvisWin

procedure TInvisWin.WndProc(var Message: TMessage);
var
ps : TPaintStruct;
begin
case Message.Msg of
WM_ERASEBKGND: Message.Result := 1;
WM_PAINT: begin
BeginPaint(Handle, ps);
EndPaint(Handle, ps);
Message.Result := 1;
end;
else
inherited WndProc(Message);
end;
end;

procedure TInvisWin.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle or WS_EX_TRANSPARENT;
end;

procedure TInvisWin.WMDLGCode(var Message: TMessage);
begin
Message.Result := DLGC_WANTARROWS or DLGC_WANTALLKEYS;
end;


// *****************************************************************
// TMover

procedure TMover.Show;
begin
Assert(Buddy <> nil);
BoundsRect := Buddy.BoundsRect;
Parent := Buddy.Parent;
Visible := True;
BringToFront;
end;


// *****************************************************************
// TResizer

constructor TResizer.Create(AOwner: TComponent);
begin
inherited;
FActive := True;
FKeepIn := True;
FGridX := GRIDDEFAULT;
FGridY := GRIDDEFAULT;
FAllowSize := True;
FAllowMove := True;
GroupMovers := TList.Create;
Sizers := TList.Create;

OneMover := TMover.Create(Self);
CreateOneMover(OneMover, nil);

CreateSizers;
end;

destructor TResizer.Destroy;
begin
GroupMovers.Free;
Sizers.Free;
Sizers := nil;
inherited;
end;

procedure TResizer.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if csDestroying in ComponentState then exit;
if (AComponent = ResizeControl) and (Operation = opRemove) then
ResizeControl := nil;
end;

procedure TResizer.SetActive(b: boolean);
begin
if b<>FActive then begin
FActive := b;
CheckSizers;
end;
end;

procedure TResizer.SetControl(c: TControl);
begin
if c <> FControl then begin

if c<>nil then begin
if ResizeGroup<>nil then begin
Assert(c.Parent = ResizeGroup, 'ResizeControl is not in ResizeGroup!');
CurMover := FindMoverByBuddy(c);
end else begin
CurMover := OneMover;
CurMover.Buddy := c;
end;
CurMover.Show;
end;

FControl := c;
CheckSizers;
end;
end;

procedure TResizer.SetGroup(p: TWinControl);
begin
if p <> FGroup then begin
FGroup := p;
CreateGroupMovers;
end;
end;

procedure TResizer.CreateGroupMovers;
var
i : integer;
m : TMover;
c : TControl;
begin
if csDesigning in ComponentState then exit;

// Clear out the old Movers
for i := 0 to GroupMovers.Count-1 do
TObject(GroupMovers).Free;
GroupMovers.Clear;

if ResizeGroup <> nil then begin
for i := 0 to ResizeGroup.ControlCount-1 do begin
c := ResizeGroup.Controls;
if (c is TMover) or (c is TSizer) then continue;

m := TMover.Create(Self);
CreateOneMover(m, c);
GroupMovers.Add(m);
m.Show;
end;
end;
end;

procedure TResizer.CreateSizers;
var
i : integer;
p : TSizer;
begin
if csDesigning in ComponentState then exit;

for i := 0 to 7 do begin
p := TSizer.Create(Self);
Sizers.Add(p);

p.BevelOuter := bvNone;
p.Width := SIZE;
p.Height := SIZE;
p.Color := clBlack;
p.Caption := '';
p.Tag := i;
p.OnMouseDown := SizerDown;
p.OnMouseUp := SizerUp;
p.OnMouseMove := SizerMove;
p.TabStop := False;

case i of
0, 7 : p.Cursor := crSizeNWSE;
2, 5 : p.Cursor := crSizeNESW;
1, 6 : p.Cursor := crSizeNS;
3, 4 : p.Cursor := crSizeWE;
end;
end;
end;

procedure TResizer.CreateOneMover(m: TMover; c: TControl);
begin
m.OnMouseDown := MoverDown;
m.OnMouseUp := MoverUp;
m.OnMouseMove := MoverMove;
m.TabStop := True;
m.OnKeyDown := MoverKeyDown;
m.Buddy := c;
end;

procedure TResizer.CheckSizers;
begin
if (ResizeControl<>nil) and Active and (not (csDesigning in ComponentState)) then
ShowSizers
else
HideSizers;
end;

procedure TResizer.ShowSizers;
var
i : integer;
p : TPanel;
c : TControl;
begin
c := ResizeControl;
Assert(c <> nil);

for i := 0 to 7 do begin
p := TPanel(Sizers);
case i of
0, 1, 2 : p.Top := c.Top - HALFSIZE;
3, 4 : p.Top := c.Top + c.Height div 2 - HALFSIZE;
5, 6, 7 : p.Top := c.Top + c.Height - HALFSIZE;
end;

case i of
0, 3, 5 : p.Left := c.Left - HALFSIZE;
1, 6 : p.Left := c.Left + c.Width div 2 - HALFSIZE;
2, 4, 7 : p.Left := c.Left + c.Width - HALFSIZE;
end;
end;

Assert(CurMover<>nil);
CurMover.Show;

for i := 0 to Sizers.Count-1 do begin
p := TPanel(Sizers);
p.Parent := c.Parent;
p.Visible := True;
p.BringToFront;
end;

if CurMover.HandleAllocated and CurMover.CanFocus then
CurMover.SetFocus;
end;

procedure TResizer.HideSizers;
var
i : integer;
p : TPanel;
begin
for i := 0 to Sizers.Count-1 do begin
p := TPanel(Sizers);
p.Visible := False;
p.Update;
end;
OneMover.Visible := False;
end;

procedure TResizer.SizerDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Sizing := True;
DownX := X;
DownY := Y;
HideSizers;
ResizeControl.Parent.Update;
ResizeControl.Update;
OrigSize := ResizeControl.BoundsRect;
NewSize := OrigSize;
DrawSizeRect(NewSize);
end;

procedure DoSwap(DoSwap: boolean; var a, b: integer);
var
t : integer;
begin
if DoSwap then begin
t := a;
a := b;
b := t;
end;
end;

procedure TResizer.SizerUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if NewSize.Right < NewSize.Left then
DoSwap(True, NewSize.Right, NewSize.Left);
if NewSize.Bottom < NewSize.Top then
DoSwap(True, NewSize.Bottom, NewSize.Top);

Sizing := False;
DrawSizeRect(NewSize);
ResizeControl.Invalidate;
ResizeControl.BoundsRect := NewSize;
ShowSizers;
if Assigned(OnSized) then OnSized(Self);
end;

procedure TResizer.SizerMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Sizing then begin
DrawSizeRect(NewSize);

if AllowSize then begin
Calc_Size_Rect((Sender as TSizer).Tag, X - DownX, Y - DownY);
DoSizingEvent;
end;

DrawSizeRect(NewSize);
if HotTrack then ResizeControl.BoundsRect := NewSize;
end;
end;

procedure TResizer.DoSizingEvent;
var
tmpWid, tmpHgt : integer;
begin
tmpWid := NewSize.Right - NewSize.Left;
tmpHgt := NewSize.Bottom - NewSize.Top;
if Assigned(OnSizing) then
OnSizing(Self, NewSize.Left, NewSize.Top, tmpWid, tmpHgt);
NewSize.Right := NewSize.Left + tmpWid;
NewSize.Bottom := NewSize.Top + tmpHgt;
end;

procedure GetNonClientOffset(h: THandle; var nx, ny: integer);
var
p : TPoint;
R : TRect;
begin
p := Point(0, 0);
Windows.ClientToScreen(h, p);
Windows.GetWindowRect(h, R);
nx := p.x - R.Left;
ny := p.y - R.Top;
end;

procedure TResizer.DrawSizeRect(Rect: TRect);
var
h : THandle;
dc : THandle;
c : TCanvas;
nx, ny : integer;
OldPen : TPen;
OldBrush : TBrush;
begin
if HotTrack then exit;

h := (ResizeControl.Parent as TWinControl).Handle;
GetNonClientOffset(h, nx, ny);
dc := GetWindowDC(h);
try
c := TCanvas.Create;
c.Handle := dc;

OldPen := TPen.Create;
OldPen.Assign(c.Pen);
OldBrush := TBrush.Create;
OldBrush.Assign(c.Brush);

c.Pen.Width := 2;
c.Pen.Mode := pmXOR;
c.Pen.Color := clWhite;
c.Brush.Style := bsClear;
c.Rectangle(Rect.Left + nx, Rect.Top + ny, Rect.Right + nx, Rect.Bottom + ny);

c.Pen.Assign(OldPen);
OldPen.Free;
c.Brush.Assign(OldBrush);
OldBrush.Free;

c.Handle := 0;
c.Free;
finally
ReleaseDC(h, dc);
end;
end;

procedure TResizer.Calc_Size_Rect(SizerNum, dx, dy: integer);
begin
dx := (dx div GridX) * GridX;
dy := (dy div GridY) * GridY;

case SizerNum of
0, 1, 2 : NewSize.Top := OrigSize.Top + dy;
5, 6, 7 : NewSize.Bottom := OrigSize.Bottom + dy;
end;

case SizerNum of
0, 3, 5 : NewSize.Left := OrigSize.Left + dx;
2, 4, 7 : NewSize.Right := OrigSize.Right + dx;
end;

if KeepInParent then Constrain_Size;
end;

procedure TResizer.MoverDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
CurMover := Sender as TMover;
FControl := CurMover.Buddy;
Assert(FControl<>nil);
FControl.BringToFront;
CurMover.BringToFront;

Moving := True;
DownX := X;
DownY := Y;
HideSizers;
ResizeControl.Parent.Update;
ResizeControl.Update;
OrigSize := ResizeControl.BoundsRect;
NewSize := OrigSize;
DrawSizeRect(NewSize);
end;

procedure TResizer.MoverUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Moving := False;
ResizeControl.BoundsRect := NewSize;
CurMover.Invalidate;
ResizeControl.Refresh;
DrawSizeRect(NewSize);
ShowSizers;
if Assigned(OnMoved) then OnMoved(Self);
end;

procedure TResizer.Calc_Move_Rect(dx, dy: integer);
begin
NewSize := OrigSize;
dx := (dx div GridX) * GridX;
dy := (dy div GridY) * GridY;
OffsetRect(NewSize, dx, dy);
if KeepInParent then Constrain_Move;
end;

procedure TResizer.DoMovingEvent;
var
tmpWid, tmpHgt : integer;
begin
tmpWid := NewSize.Right - NewSize.Left;
tmpHgt := NewSize.Bottom - NewSize.Top;
if Assigned(OnMoving) then
OnMoving(Self, NewSize.Left, NewSize.Top);
NewSize.Right := NewSize.Left + tmpWid;
NewSize.Bottom := NewSize.Top + tmpHgt;
end;

procedure TResizer.MoverMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
dx, dy: integer;
begin
if Moving then begin
DrawSizeRect(NewSize);

if AllowMove then begin
dx := X - DownX;
dy := Y - DownY;
Calc_Move_Rect(dx, dy);
DoMovingEvent;
end;

DrawSizeRect(NewSize);
if HotTrack then ResizeControl.BoundsRect := NewSize;
end;
end;

procedure TResizer.Constrain_Size;
var
p : TWinControl;
begin
p := ResizeControl.Parent;

with NewSize do begin
if Left < 0 then Left := 0;
if Top < 0 then Top := 0;
if Right > p.ClientWidth then Right := p.ClientWidth;
if Bottom > p.ClientHeight then Bottom := p.ClientHeight;

if Right < Left + GridX then Right := Left + GridX;
if Bottom < Top + GridY then Bottom := Top + GridY;
end;
end;

procedure TResizer.Constrain_Move;
begin
if NewSize.Left < 0 then
OffsetRect(NewSize, -NewSize.Left, 0);

if NewSize.Top < 0 then
OffsetRect(NewSize, 0, -NewSize.Top);

if NewSize.Right > ResizeControl.Parent.ClientWidth then
OffsetRect(NewSize, ResizeControl.Parent.ClientWidth - NewSize.Right, 0);

if NewSize.Bottom > ResizeControl.Parent.ClientHeight then
OffsetRect(NewSize, 0, ResizeControl.Parent.ClientHeight - NewSize.Bottom);
end;

procedure TResizer.MoverKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Active then begin
case Key of
VK_LEFT : DoSizeMove(Key, Shift, -GridX, 0);
VK_RIGHT : DoSizeMove(Key, Shift, GridX, 0);
VK_UP : DoSizeMove(Key, Shift, 0, -GridY);
VK_DOWN : DoSizeMove(Key, Shift, 0, GridY);
end;
end;
end;

procedure TResizer.DoSizeMove(var Key: Word; Shift: TShiftState; dx, dy: integer);
begin
if (ssCtrl in Shift) or (ssShift in Shift) then begin
Key := 0;

NewSize := ResizeControl.BoundsRect;

if (ssCtrl in Shift) and AllowMove then begin
OffsetRect(NewSize, dx, dy);
if KeepInParent then Constrain_Move;
DoMovingEvent;
end;

if (ssShift in Shift) and AllowSize then begin
NewSize.Right := NewSize.Right + dx;
NewSize.Bottom := NewSize.Bottom + dy;
if KeepInParent then Constrain_Size;
DoSizingEvent;
end;

ResizeControl.BoundsRect := NewSize;
ShowSizers;
end;
end;

function TResizer.FindMoverByBuddy(c: TControl): TMover;
var
i : integer;
begin
Result := nil;
for i := 0 to GroupMovers.Count-1 do
if TMover(GroupMovers).Buddy = c then
Result := GroupMovers;
Assert(Result <> nil);
end;

end.
 
哪里看得懂嘛!!!
谁能做个例子啊,就实现form里面的panel在拖动位置的时候像QQ那样有虚线框就行了。谢谢!!!
 
procedure TForm1.FormDragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
if Source is Tpanel then Accept := true;
end;

procedure TForm1.FormDragDrop(Sender, Source: TObject; X, Y: Integer);
begin
(Source as Tpanel).Left := x;
(Source as Tpanel).top := y;
end;
 
对了,还要把pannel1的dragmode属性设为dmAutomatic

如果想要虚线的,那就只有自己做一个cursor 资源
Panel1.DragCursor := 自定义cursor 资源
 
to ak_2004:
你说的那个是个办法,但总觉得有欺骗性质,呵呵[:D]
能不能就用代码实现?怎么自己做嘛?
 
ak_2004的方法不可行,pannel大小是不确定的,怎么做cursor 资源?!
还是老老实实写代码吧。
 
to ak_2005:
就是,能不能做个例子发给我呀,非常感谢
 
怎么会呢? 我不明白你为什么说有欺骗性质
设置了panel1.dragcursor 那么它在拖动时,光标就自动变成你设置成的值,在不能拖动或结束时又会恢复
,你可以试一试
其实QQ的那个拖动,也是一样的嘛

还有你说的用代码实现,这是什么意思哦/不懂。难道我那不是用代码实现的吗? 你该不是说在拖动时,自己去画那个虚线筐吧。那样是不是太麻烦了。而且效果不是一样的吗?
 
to ak_2004:
你把QQ窗口的大小改变再拖动,是不是虚线框的大小也随着变了的呀,ak_2005说的就是这个意思!如果事先做一个的话,就不能随着panel的大小而改变了!!!
 
ak_2004,ak_2005你们什么关系啊!不会是兄弟吧
 
哪天....ak_2003....ak_2006....整个ak家族都跑出来有看头.呵呵
 
直接画屏幕吧(画虚线框),鼠标有坐标,控件有宽和高,其实就是坐标的操作了。
//期待ak家族能够庞大起来 [:D]
 
不是应该用画的方法解决吧???
 
我最近也需要这样的东西,有结果告诉一声,先占个地方
 
顶部