PeterLarsen
asked on
Hook two forms to eachother, like Winamp player do !
Hi Experts,
I have two forms that i would like to move simultaneously when i move one of the forms in run-time.
If i remember right, there is a windows function that can do that.
Any suggestions.
Regards
Peter
I have two forms that i would like to move simultaneously when i move one of the forms in run-time.
If i remember right, there is a windows function that can do that.
Any suggestions.
Regards
Peter
TMagnetic v.1.01
By Zhong Wan. TMagnetic allows you move form like Winamp does, i.e. forms has ability to be docked to Screen, Explorer, TrayBar and other applications. Also you can change size of the forms without captions.
Fully functional
Source: Included
Exe-demo included
http://www.torry.net/vcl/forms/effects/zmagnetic.zip
best regards
Cesario
By Zhong Wan. TMagnetic allows you move form like Winamp does, i.e. forms has ability to be docked to Screen, Explorer, TrayBar and other applications. Also you can change size of the forms without captions.
Fully functional
Source: Included
Exe-demo included
http://www.torry.net/vcl/forms/effects/zmagnetic.zip
best regards
Cesario
ASKER
Hi Cesario,
Nice little thing you have there :-)
But this is pretty much what i already have by now.
What i want is some functions closer to the windows OS.
/PL
Nice little thing you have there :-)
But this is pretty much what i already have by now.
What i want is some functions closer to the windows OS.
/PL
Sample code comes in 30 min ;-)
Cesario
Cesario
ASKER
I will look into your second suggestions later today !!
ASKER
hehe, you'r fast Cesario !!
Here is the exmaple
Magnetic Window
by Wang Zhen- delphiques@email.com
One who uses Winamp may find it quite interesting that all of its child windows (the equalizer, the playlist or the mini-browser) will be aligned automatically when they are close to each other, just as if they are magnetic. After an hour's thinking and exploring, I've finally found a way to simulate this cool feature in my plug-in for Winamp. If you also want to equip your application with it, please have a look on the demonstration program below to get a general idea of my solution. The demo form will be "attracted" by Winamp when dragged near to Winamp's main window. It runs OK in Delphi 3 and should work well in any other versions, I suppose.
First, set up a new application and change the BorderStyle of Form1 to bsNone. Throw a button onto the form. Double click it and write "close;". Later, you will find it useful when you get bored. Now, let's do some typing. Here are the global variables.
var
Form1: TForm1; //yeah, the magnetic window...
LastX, LastY: Integer; //keep the coordinates of cursor
WinampRect:TRect; //stores the region of target window
hwnd_Winamp:HWND; //the handle of Winamp's main window
Then fill the OnMouseDown and OnMouseMove events of Form1 as shown below.
procedure TForm1.FormMouseDown(Sende r: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
const
ClassName='Winamp v1.x'; //the classname of Winamp's main window
{ Replace the line with "ClassName='TAppBuilder'" if your Delphi itself hungers for some magnetic power. }
begin
//save the coordinates
LastX := X;
LastY := Y;
//try to locate Winamp
hwnd_Winamp := FindWindow(ClassName,nil);
if hwnd_Winamp>0 then
GetWindowRect(hwnd_Winamp, WinampRect);
end;
procedure TForm1.FormMouseMove(Sende r: TObject; Shift: TShiftState; X, Y: Integer);
var
nLeft,nTop:integer;
begin
//check whether the form is being dragged
if HiWord(GetAsyncKeyState(VK _LBUTTON)) > 0 then
begin
//calculate new position
nleft := Left + X - LastX;
nTop := Top + Y - LastY;
//adjust the position if Winamp is found
ifhwnd_Winamp>0 then
Magnetize(nleft,ntop);
SetBounds(nLeft,nTop,width ,height);
end;
end;
Have a pause here. Before unveiling the codes of "magnetize()", I wanna give an unofficial definition of "window-magnetization". That is, "a process of adjusting the position of source window to align it and target window on one plane surface when they are close to some predefined extent". A little bit confusing huh? Well, sample codes are sometimes better than dull explanation. Let's continue...
procedure TForm1.Magnetize(var nl,nt:integer);
//two simple embedded functions
function Min(a,b:integer):integer;
begin
ifa>b then result:=b else result:=a;
end;
function Max(a,b:integer):integer;
begin
if a < b then result:=b else result:=a;
end;
var
H_Overlapped,V_Overlapped: boolean;
tw,ww,wh:integer;
const
MagneticForce:integer=50;
{ in other words, when two windows are nearer than 50 pixels, they will be aligned automatically. Remember, 50 is used for demonstration purposes only.
It's quite exaggerative if you want to simulate the effect of Winamp. }
begin
ww := WinampRect.Right-WinampRec t.Left;
tw := Max(WinampRect.Right,nl+Wi dth)-Min(W inampRect. Left,nl);
H_Overlapped := tw<=(Width+ww);
wh := WinampRect.Bottom-WinampRe ct.Top;
tw := Max(WinampRect.Bottom,nt+H eight)-Min (WinampRec t.Top,nt);
V_Overlapped := tw<=(Height+wh);
//change the position when the two windows are close enough
if H_Overlapped then
begin
if Abs(WinampRect.Bottom-nt)< MagneticForce then nt := WinampRect.Bottom
else if Abs(nt+Height-WinampRect.T op)< MagneticForce then nt := WinampRect.Top-Height;
end;
if V_Overlapped then
begin
if Abs(WinampRect.Right-nl)< MagneticForce then nl := WinampRect.Right
else if Abs(nl+Width-WinampRect.Le ft)< MagneticForce then nl := WinampRect.Left-Width;
end;
end;
Voila, now press F9 and feel it!
Magnetic Window
by Wang Zhen- delphiques@email.com
One who uses Winamp may find it quite interesting that all of its child windows (the equalizer, the playlist or the mini-browser) will be aligned automatically when they are close to each other, just as if they are magnetic. After an hour's thinking and exploring, I've finally found a way to simulate this cool feature in my plug-in for Winamp. If you also want to equip your application with it, please have a look on the demonstration program below to get a general idea of my solution. The demo form will be "attracted" by Winamp when dragged near to Winamp's main window. It runs OK in Delphi 3 and should work well in any other versions, I suppose.
First, set up a new application and change the BorderStyle of Form1 to bsNone. Throw a button onto the form. Double click it and write "close;". Later, you will find it useful when you get bored. Now, let's do some typing. Here are the global variables.
var
Form1: TForm1; //yeah, the magnetic window...
LastX, LastY: Integer; //keep the coordinates of cursor
WinampRect:TRect; //stores the region of target window
hwnd_Winamp:HWND; //the handle of Winamp's main window
Then fill the OnMouseDown and OnMouseMove events of Form1 as shown below.
procedure TForm1.FormMouseDown(Sende
Shift: TShiftState; X, Y: Integer);
const
ClassName='Winamp v1.x'; //the classname of Winamp's main window
{ Replace the line with "ClassName='TAppBuilder'" if your Delphi itself hungers for some magnetic power. }
begin
//save the coordinates
LastX := X;
LastY := Y;
//try to locate Winamp
hwnd_Winamp := FindWindow(ClassName,nil);
if hwnd_Winamp>0 then
GetWindowRect(hwnd_Winamp,
end;
procedure TForm1.FormMouseMove(Sende
var
nLeft,nTop:integer;
begin
//check whether the form is being dragged
if HiWord(GetAsyncKeyState(VK
begin
//calculate new position
nleft := Left + X - LastX;
nTop := Top + Y - LastY;
//adjust the position if Winamp is found
ifhwnd_Winamp>0 then
Magnetize(nleft,ntop);
SetBounds(nLeft,nTop,width
end;
end;
Have a pause here. Before unveiling the codes of "magnetize()", I wanna give an unofficial definition of "window-magnetization". That is, "a process of adjusting the position of source window to align it and target window on one plane surface when they are close to some predefined extent". A little bit confusing huh? Well, sample codes are sometimes better than dull explanation. Let's continue...
procedure TForm1.Magnetize(var nl,nt:integer);
//two simple embedded functions
function Min(a,b:integer):integer;
begin
ifa>b then result:=b else result:=a;
end;
function Max(a,b:integer):integer;
begin
if a < b then result:=b else result:=a;
end;
var
H_Overlapped,V_Overlapped:
tw,ww,wh:integer;
const
MagneticForce:integer=50;
{ in other words, when two windows are nearer than 50 pixels, they will be aligned automatically. Remember, 50 is used for demonstration purposes only.
It's quite exaggerative if you want to simulate the effect of Winamp. }
begin
ww := WinampRect.Right-WinampRec
tw := Max(WinampRect.Right,nl+Wi
H_Overlapped := tw<=(Width+ww);
wh := WinampRect.Bottom-WinampRe
tw := Max(WinampRect.Bottom,nt+H
V_Overlapped := tw<=(Height+wh);
//change the position when the two windows are close enough
if H_Overlapped then
begin
if Abs(WinampRect.Bottom-nt)<
else if Abs(nt+Height-WinampRect.T
end;
if V_Overlapped then
begin
if Abs(WinampRect.Right-nl)< MagneticForce then nl := WinampRect.Right
else if Abs(nl+Width-WinampRect.Le
end;
end;
Voila, now press F9 and feel it!
Here is the right Example:
How can I reposition a form relative to another form, which is being dragged by the mouse? I am thinking on a kind of movement synchronization. TControl.WMMove is unfortunately declared private.
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(@SRGSB adUseOfFF) ;
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(FOldOwnerWindowPr oc) then
begin
if (FOwnerForm <> nil) and (FOwnerForm.HandleAllocate d) 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(Se nder: TObject);
begin
Close;
end;
end.
Tip by Ralph Friedman
How can I reposition a form relative to another form, which is being dragged by the mouse? I am thinking on a kind of movement synchronization. TControl.WMMove is unfortunately declared private.
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(@SRGSB
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
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(FOldOwnerWindowPr
begin
if (FOwnerForm <> nil) and (FOwnerForm.HandleAllocate
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
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(Se
begin
Close;
end;
end.
Tip by Ralph Friedman
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
the Rect1 var is for someting else and should not be there
ASKER
Hi all,
And thanks for your comments.
As i wrote earlier, i want functions closer to the OS.
I use DeferWindowPos myself, which prevent flickering and separation between the two (or more) forms.
But it doesn't look good in older OS (Windows OS that dont move the entire form, but only a frame). You gotta see it before you understand why.
Anyway - i need a better way to capture the move (other than WM_Moving) or a better way to control the moving FocusRect myself.
/Peter
And thanks for your comments.
As i wrote earlier, i want functions closer to the OS.
I use DeferWindowPos myself, which prevent flickering and separation between the two (or more) forms.
But it doesn't look good in older OS (Windows OS that dont move the entire form, but only a frame). You gotta see it before you understand why.
Anyway - i need a better way to capture the move (other than WM_Moving) or a better way to control the moving FocusRect myself.
/Peter
ASKER
Well, i can't say that you have answered my question, but...
I have found that SystemParametersInfo.SPI_G ETDRAGFULL WINDOWS tells whether a moving window is repainted or only shown by a FocusRect.
If the window is repainted i use WM_MOVING and DeferWindowPos. DeferWindowPos is easy to use when the number of moving windows are unknown. Also the ability to move the windows at the exact same time is important
If only a FocusRect is shown i use WM_MOVING and WM_MOVE.
The code provided by Slick812 is the simplest way to move two windows simultaneously. So the points go to him.I have taken the idea and use it when the system dont meet the requirements to use DeferWindowPos, e.g. short of memory etc.
Thanks for your time.
Peter
I have found that SystemParametersInfo.SPI_G
If the window is repainted i use WM_MOVING and DeferWindowPos. DeferWindowPos is easy to use when the number of moving windows are unknown. Also the ability to move the windows at the exact same time is important
If only a FocusRect is shown i use WM_MOVING and WM_MOVE.
The code provided by Slick812 is the simplest way to move two windows simultaneously. So the points go to him.I have taken the idea and use it when the system dont meet the requirements to use DeferWindowPos, e.g. short of memory etc.
Thanks for your time.
Peter
TFormEx adds Docking capability (dock to desktop or other forms), add background image or gradient pattern, add form minimum and maximum size, Force Form to always stay on desktop, Hide caption bar, move window by clicking in client area, and window transparency.
http://www.delphispirit.com/getvcl.php?vclid=437&rd=vcl/forms/complex/faformex.zip