by Matt Hamilton - MHamilton@bunge.com.au (and a little extra by Robert Vivrette)
After some experimentation with Captionless Forms and my custom panel components, I have found a (relatively) easy way to make your component movable at runtime. That is, when a user clicks and drags on your component, a "frame" appears just like when you move a window, so they can drag the component around.
The method involves catching a Windows Message called WM_NCHITTEST, which is passed to windows whenever a mouse operation (like moving the pointer or clicking) occurs on them. Let's go through the steps:
1. In the protected section of your component, add the following line:
Keep coding!
Notes from the Editor:
When I first got this, I was reading through it and decided that no, it wasn't going to work. But sure enough... It did! Basically, this is just tricking the component into thinking that you are over its window banner area (HTCAPTION). All controls are basically "windows", it's just that most don't have banners like a form does. By overridding the WM_NCHitTest message, you are just telling the control "the mouse is now over your banner" and then all the default windows behavior kicks in for this condition.
For example, I tried this with a TEdit and it basically worked as advertised... but with a few undesireable exceptions. First, since it eats the mouse message, you can't click on such a control to give it focus (unless you modify the NCHitTest to shrink the size of the "caption" area as he did above). Also, since the component was now thinking that you were over its "banner", I wondered what would happen if I double-clicked on it. Sure enough... It maximizes the control to fill the form! Definitely got to stop that one... but it does open up some interesting possibilities.
I reviewed this message again and decided to try all the other possible message results (i.e. those other than HTCAPTION). If you open up the WIN32.HLP file and search on WC_NCHITTEST, you will see the other result flags that can be managed. For example, if you change HTCAPTION in the first example to HTBOTTOM, when the mouse moves into the control, it thinks that you are now on the bottom of the "window's" resize border and the double-headed arrow shows up. Any click and drag process within the control then resizes the bottom edge of the control. It is obvious that with just a few lines of code, you could enable all the behavior of a standard resizable window into just about any control.
For those of you who need a little help setting this up as a custom component, here is the entire code for what I did to experiment with his technique.
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs,
StdCtrls;
type
TEdit1 = class(TEdit)
protected
procedure NCHitTest(var
Msg: TWMNCHitTest); message WM_NCHITTEST;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Win32', [TEdit1]);
end;
procedure TEdit1.NCHitTest(var Msg: TWMNCHitTest);
begin
inherited;
Msg.Result := HTCAPTION;
end;
end.
The possibilities are endless. I would love to see additional work on this by some enterprising reader out there. In fact, I have a few ideas of my own... stay tuned!