I wrote this article in Arabic too, check it out here.
What is that?
Today, we are talking about how to move a form without its title bar.
You might have noticed that some applications with fancy UIs do not allow the user to move the window from its title bar. Honestly, some hide the overall title bar from the user. An example of these applications is Microsoft Windows Media Player -when in skin mode,- and Microsoft Windows Live Messenger. Both applications allow you to drag their windows using the client area not the title bar.
In this lesson, you will learn how to do this trick to move the form without its title bar.
At the end of this lesson, a sample application is available for download. It is a simple application with an outstanding UI that illustrates how to move the form using its client area.
How?
Theoretically, you cannot drag any form without its title bar. However, we can simulate the dragging process which implies clicking with the left mouse button on the title bar and moving the cursor without releasing the left mouse button.
You can simulate this process using the SendMessage()
function. This function is used to send a specific message (you can say command/order) to a specific object -specified by its handle.- This specific message can be attached with another messages (commands) to produce a new command. For example, in our lesson we will attach the message WM_NCLBUTTONDOWN
with the message HTCAPTION
to simulate the left mouse button clicking on the caption (title bar) of a window (form).
Honestly, every object is a window. Every button -even the Close button- is a window, the menu item is a window, the status bar is a window, and the tooltip is another window!
The definition of SendMessage()
is as follows:
LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);
This function takes four arguments:
hWnd
:
The handle of the object (window.) to send the message to. Can be set to a window handle, HWND_DESKTOP
(to send the message to the desktop,) HWND_BROADCAST
(to send the message to all windows.) Msg
:
The primary message that will be sent. wParam
:
A secondary message to be attached to the primary message. Set to 0
to indicate NULL
. lParam
:
Another message that can be attached to the primary message. Set to 0
to indicate NULL
. If wParam
was not set, you cannot set lParam
.
The return value of this function depends on the message sent. In our example, the function may return 0
if succeeded, or non-zero if failed.
It is worth mentioning that, in our example, another function must be called before SendMessage();
it is the ReleaseCapture()
function. This function releases the mouse capture from a window (object) and restores the normal mouse processing. A window (object) that has captured the mouse receives all mouse input. Therefore, calling the ReleaseCapture()
allows our form to release this mouse input (left-clicking) simulated.
The ReleaseCapture()
function is very simple; its definition is as follows:
BOOL ReleaseCapture(VOID);
This function returns TRUE
if succeeded, or FALSE
otherwise.
Let’s Code!
Now, we are going to put things together and mix them up.
The first step is to hide the title bar of the form. This can be done through the FormBorderStyle
property of the form. You can set this property to FormBorderStyle.None
to hide the toolbar and borders of the form.
The next step to take is to create the PInvoke methods for the functions. Here is the full code:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.I4)]
static extern int SendMessage(
IntPtr hWnd,
[param: MarshalAs(UnmanagedType.U4)]
uint Msg,
[param: MarshalAs(UnmanagedType.U4)]
uint wParam,
[param: MarshalAs(UnmanagedType.I4)]
int lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ReleaseCapture();
const uint WM_NCLBUTTONDOWN = 0xA1;
const uint HTCAPTION = 2;
Again and again, PInvoke stands for Platform Invocation; it is the CLR service that allows .NET to call unmanaged code. This service requires special changes to the PInvoke method. Also, specific rules are applied when PInvoking an unmanaged function.
The last code demonstrates the PInvoke methods for the functions, and the constants required.
The last step is to add the implementation code required to the MouseDown
event of any control that you wish to allow the user to drag the form from. For instance, you can add the code to the MouseDown
event of a PictureBox
control that will act as the new and fancy title bar. In addition, you can add the code to the form’s MouseDown
event to allow the user to drag the form from any point of its client area.
The following code demonstrates how to allow the user to drag the form from its client area:
private void MainForm_MouseDown
(object sender, MouseEventArgs e)
{
ReleaseCapture();
SendMessage(this.Handle,
WM_NCLBUTTONDOWN,
HTCAPTION,
0);
}
It is better to check the return value of every call to determine whether the operation succeeded or failed.
A Problem Occurred!
It is wonderful to allow the user to drag the form from its client area. But, what if the user tried to drag the form from a label on the client area? It will not be dragged.
To overcome this situation, you can add the same code to the MouseDown
event of the label. In this case, for better code maintenance and to avoid code repetition, you can define a method that contains the code, and call it from the MouseDown
event of every control that you need to allow the user to drag the form from such as a label
or a PictureBox
. Another solution is to create a single MouseDown
event handler for all controls that you are interested in.
Well! What’s Next?
You can use this technique when creating applications with hot skins to allow the user to drag the form from its client area or from a specific control like a PictureBox
that acts as the new hot title bar.
It is worth mentioning that you would better follow the guidelines when interoperating with unmanaged code. For example, PInvoke methods and constants should be declared inside an internal class called -in our example- SafeNativeMethods
. In addition, it is better creating a wrapper method for the PInvoke methods. For example, instead of calling many functions to drag the form, you can create a single function named -for instance- MoveForm()
that acts as a wrapper for the PInvoke methods. And that results in code that is more readable and easier to maintain. Plus, it prevents code repetition.
For more information about the functions, consult the MSDN documentation:
WOW! A Code Sample!
This is a very simple application that illustrates moving a form using its client area.
This sample has been created using Microsoft Visual Studio 2008 and .NET Framework 2.0.
The following is a snapshot from the application:
Posted in Win32 API Tagged: .NET, API, CodeProject, CSharp, Interoperability, WinForms