The Unofficial Newsletter of Delphi Users - by Robert Vivrette



Moving and Sizing Controls at Runtime

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:

2. In the implementation section of the unit, define the method thusly: That's it! Now when you click on the component at runtime, you can drag it around the form. The next thing to do is test whether the mouse has been clicked on one particular section of your component (like a "title bar" area, for example). Redefine your definition like this: This will check that the user clicked somewhere within the top 16 pixels of your component. Otherwise, the usual stuff will happen (OnClick or whatever).

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.

If you wanted to go a little further with this technique, you could add a property to a custom component that allows you to turn on and off movement and/or resizing. Then in your overridden NCHitTest method, you just test to see if it is enabled and only return the result if it is. You could then use this technique to allow users of your application to design the size and placement of controls on the form. Simply turn on a "design-mode" (which sets the Movable/Resizable property of the form's controls), and then let the user move and resize all the controls on the form. When he is done, he turns off the design-mode and your application can save the positions of the control to the registry or an INI file.

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!