2005. május 10., kedd
How to move any component at runtime
Problem/Question/Abstract:
How to move any component at runtime
Answer:
Solve 1:
There is a simple trick for allowing the user to move components at runtime. However, this will only work for components which derive from a TWinControl as it requires a Handle property. The solution I am about to give will work with ANY component. Although it uses the same method, I have achieved moving components without a handle property by temporarily placing them inside a TPanel. Make sure ExtCtrls is in your USES clause, then point the OnMouseDown event for each component at the following code:
procedure TForm1.MoveControl(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
TempPanel: TPanel;
Control: TControl;
begin
{Release the MOUSEDOWN status}
ReleaseCapture;
{If the component is a TWinControl, just move it directly}
if Sender is TWinControl then
TWinControl(Sender).Perform(WM_SysCommand, $F012, 0)
else
try
Control := TControl(Sender);
TempPanel := TPanel.Create(Self);
with TempPanel do
begin
{Replace the component with TempPanel}
Caption := '';
BevelOuter := bvNone;
SetBounds(Control.Left, Control.Top, Control.Width, Control.Height);
Parent := Control.Parent;
{Put our control in TempPanel}
Control.Parent := TempPanel;
{Move TempPanel with control inside of it}
Perform(WM_SysCommand, $F012, 0);
{Put the component where the panel was dropped}
Control.Parent := Parent;
Control.Left := Left;
Control.Top := Top;
end;
finally
TempPanel.Free;
end;
end;
Solve 2:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type
TControlDragKind = (dkNone, dkTopLeft, dkTop, dkTopRight, dkRight, dkBottomRight,
dkBottom, dkBottomLeft, dkLeft, dkClient);
TForm1 = class(TForm)
procedure FormClick(Sender: TObject);
private
{ Private declarations }
FDownPos: TPoint; { position of last mouse down, screen-relative }
FDragKind: TcontrolDragKind; { kind of drag in progress }
procedure ControlMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure ControlMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
function GetDragging: Boolean;
public
{ Public declarations }
property DraggingControl: Boolean read GetDragging;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
const
{ Set of cursors to use while moving over and dragging on controls. }
DragCursors: array[TControlDragKind] of TCursor =
(crDefault, crSizeNWSE, crSizeNS, crSizeNESW, crSizeWE,
crSizeNWSE, crSizeNS, crSizeNESW, crSizeWE, crHandPoint);
{Width of "hot zone" for dragging around the control borders. }
HittestMargin = 3;
type
TCracker = class(TControl); { Needed since TControl.MouseCapture is protected }
{ Perform hittest on the mouse position. Position is in client coordinates for the passed control. }
function GetDragKind(control: TControl; X, Y: Integer): TControlDragKind;
var
r: TRect;
begin
r := control.Clientrect;
Result := dkNone;
if Abs(X - r.left) <= HittestMargin then
if Abs(Y - r.top) <= HittestMargin then
Result := dkTopLeft
else if Abs(Y - r.bottom) <= HittestMargin then
Result := dkBottomLeft
else
Result := dkLeft
else if Abs(X - r.right) <= HittestMargin then
if Abs(Y - r.top) <= HittestMargin then
Result := dkTopRight
else if Abs(Y - r.bottom) <= HittestMargin then
Result := dkBottomRight
else
Result := dkRight
else if Abs(Y - r.top) <= HittestMargin then
Result := dkTop
else if Abs(Y - r.bottom) <= HittestMargin then
Result := dkBottom
else if PtInRect(r, Point(X, Y)) then
Result := dkClient;
end;
procedure TForm1.FormClick(Sender: TObject);
var
pt: TPoint;
begin
{get cursor position, convert to client coordinates}
GetCursorPos(pt);
pt := ScreenToClient(pt);
{create label with top left corner at mouse position}
with TLabel.Create(Self) do
begin
Autosize := False; { Otherwise resizing is futile. }
SetBounds(pt.x, pt.y, width, height);
Caption := Format('Hit at %d, %d', [pt.x, pt.y]);
Color := clBlue;
Font.Color := clWhite;
Parent := Self;
{attach the drag handlers}
OnMouseDown := ControlMouseDown;
OnMouseUp := ControlMouseUp;
OnMouseMove := ControlMouseMove;
end;
end;
procedure TForm1.ControlMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
{ Go into drag mode if left mouse button went down and no modifier key is pressed. }
if (Button = mbLeft) and (Shift = [ssLeft]) then
begin
{ Determine where on the control the mouse went down. }
FDragKind := GetDragKind(Sender as TControl, X, Y);
if FDragKind <> dkNone then
begin
with TCracker(Sender) do
begin
{ Record current position screen-relative, the origin for the client-relative position will move if the form is moved or resized on left/top sides. }
FDownPos := ClientToScreen(Point(X, Y));
MouseCapture := True;
Color := clRed;
end;
end;
end;
end;
procedure TForm1.ControlMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
dx, dy: Integer;
pt: TPoint;
r: TRect;
begin
{ Set controls cursor depending on position in control. }
(Sender as TControl).Cursor := DragCursors[GetDragKind(TControl(Sender), X, Y)];
{ If we are dragging the control, get amount the mouse has moved since last call
and calculate a new boundsrect for the control from it, depending on drag mode. }
if DraggingControl then
with Sender as TControl do
begin
pt := ClientToScreen(Point(X, Y));
dx := pt.X - FDownPos.X;
dy := pt.Y - FDownPos.Y;
{ Update stored mouse position to current position. }
FDownPos := pt;
r := BoundsRect;
case FDragKind of
dkTopLeft:
begin
r.Left := r.Left + dx;
r.Top := r.Top + dy;
end;
dkTop:
begin
r.Top := r.Top + dy;
end;
dkTopRight:
begin
r.Right := r.Right + dx;
r.Top := r.Top + dy;
end;
dkRight:
begin
r.Right := r.Right + dx;
end;
dkBottomRight:
begin
r.Right := r.Right + dx;
r.Bottom := r.Bottom + dy;
end;
dkBottom:
begin
r.Bottom := r.Bottom + dy;
end;
dkBottomLeft:
begin
r.Left := r.Left + dx;
r.Bottom := r.Bottom + dy;
end;
dkLeft:
begin
r.Left := r.Left + dx;
end;
dkClient:
begin
OffsetRect(r, dx, dy);
end;
end;
{ Don't let the control be resized to nothing }
if ((r.right - r.left) > 2 * HittestMargin) and ((r.bottom - r.top) > 2 *
HittestMargin) then
Boundsrect := r;
end;
end;
procedure TForm1.ControlMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if DraggingControl then
begin
{ Revert to non-dragging state. }
FDragKind := dkNone;
with TCracker(Sender) do
begin
MouseCapture := False;
Color := clBlue;
end;
end;
end;
{ Read method for ControlDragging property, returns true if form is in drag mode. }
function TForm1.GetDragging: Boolean;
begin
Result := FDragKind <> dkNone;
end;
end.
Solve 3:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
LastX, LastY: Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
with (Sender as TPanel) do
begin
if csLButtonDown in ControlState then
begin
Left := ScreenToClient(Point(ClientToScreen(Point(Left, Top)).X,
ClientToScreen(Point(Left, Top)).Y)).X + (X - LastX);
Top := ScreenToClient(Point(ClientToScreen(Point(Left, Top)).X,
ClientToScreen(Point(Left, Top)).Y)).Y + (Y - LastY);
end;
end;
end;
procedure TForm1.Panel1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
LastX := X;
LastY := Y;
end;
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése