Synchronize the movement of two forms (Views: 33)
Problem/Question/Abstract: How can I reposition a form relative to another form, which is being dragged by the mouse? I am thinking of a kind of movement synchronization. TControl.WMMove is unfortunately declared private. Answer: The following is a primitive example, but it should get you started: unit FollowForm; interface uses Windows, Messages, SysUtils, Classes, Graphics, Forms, Controls, Buttons, StdCtrls, ExtCtrls; type TFrmFollow = class(TForm) BtnValidate: TBitBtn; BtnSave: TBitBtn; BtnPreview: TBitBtn; BtnPrint: TBitBtn; BtnExit: TBitBtn; BtnHelp: TBitBtn; procedure BtnExitClick(Sender: TObject); private FOldOwnerWindowProc: TWndMethod; {WindowProc for FOwnerForm} FOwnerForm: TForm; {Window subclassing methods:} procedure HookForm; procedure UnhookForm; procedure WndProcForm(var AMsg: TMessage); protected procedure CreateWnd; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; end; var FrmFollow: TFrmFollow; implementation {$R *.DFM} resourcestring SRGSBadUseOfFF = 'FollowForm can only be owned by another form'; constructor TFrmFollow.Create(AOwner: TComponent); begin inherited Create(AOwner); if AOwner <> nil then begin if AOwner is TForm then FOwnerForm := TForm(AOwner) else {Owner is not a form} raise Exception.CreateRes(@SRGSBadUseOfFF); end; end; procedure TFrmFollow.CreateWnd; begin inherited; if csDesigning in ComponentState then Exit; {Don't need to hook when designing} if Enabled and Assigned(FOwnerForm) then HookForm; {Hook the main form's Window} end; destructor TFrmFollow.Destroy; begin if not (csDesigning in ComponentState) then UnhookForm; {Stop interfering ...} inherited Destroy; end; procedure TFrmFollow.HookForm; begin {Hook the windows procedure of my owner only if I have an owner, the Owner's window handle has been created and we are not in design mode.} FOldOwnerWindowProc := nil; if Assigned(FOwnerForm) and FOwnerForm.HandleAllocated then begin if not (csDesigning in ComponentState) then begin FOldOwnerWindowProc := FOwnerForm.WindowProc; FOwnerForm.WindowProc := WndProcForm; end; end; end; procedure TFrmFollow.UnhookForm; begin {If we are "hooked" then undo what Hookform did} if Assigned(FOldOwnerWindowProc) then begin if (FOwnerForm <> nil) and (FOwnerForm.HandleAllocated) then begin FOwnerForm.WindowProc := FOldOwnerWindowProc; end; FOldOwnerWindowProc := nil; FOwnerForm := nil; end; end; {WndProcForm is our replacement for our WindowProc. We grab any Windows messages that we need here.} procedure TFrmFollow.WndProcForm(var AMsg: TMessage); var cmdType: Word; xPos: Word; yPos: Word; begin if Enabled then begin case AMsg.Msg of WM_MOVE: begin xPos := FOwnerForm.Left; yPos := FOwnerForm.Top; Caption := Format('%d:%d', [xPos, yPos]); SetBounds(xPos + 12, yPos + 12, Width, Height); BringToFront; end; WM_SIZE, WM_EXITSIZEMOVE: begin BringToFront; end; WM_SYSCOMMAND: begin cmdType := AMsg.WParam and $FFF0; case cmdType of SC_MAXIMIZE, SC_SIZE: begin xPos := FOwnerForm.Left; yPos := FOwnerForm.Top; Caption := Format('%d:%d', [xPos, yPos]); SetBounds(xPos, yPos, Width, Height); BringToFront; end; end; end; end; end; {Call the default windows procedure} FOldOwnerWindowProc(AMsg); end; procedure TFrmFollow.BtnExitClick(Sender: TObject); begin Close; end; end. |