Click here to Skip to main content
Click here to Skip to main content

Tagged as

Ultimate solution for moving a form by clicking anywhere in the window

, 26 Nov 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
Ultimate solution for moving a form by clicking anywhere in the window

Introduction

Normally a window is moved by clicking on the caption bar and dragging. Occasionally, people want to move borderless forms, or, move a form by clicking anywhere in the form without the limitation to the caption bar. There are many ways to do it. Two most commonly seen solutions are:

  1. Handle the mousedown, mouseup, mousemove events. You can mark a flag of drag in mousedown, record the initial mouse coordinates as the start point. In mousemove event, calculate the delta between the current mouse position and the start point. Set the window's new location using this delta as deviation value. Then the window will follow the mouse movement. And finally, set the drag flag to false to turn off the dragging.

    It's working. But you need to handle three events and use flags and variables to accomplish the work.

  2. A simpler way is to intercept the message loop of a form and retarget the mouse events hitting the client area to the caption bar, as shown in the following code snippet:
    private</span /> const</span /> int</span /> WM_NCHITTEST = 0x0084;
    private</span /> const</span /> int</span /> HTCLIENT = 1</span />;
    private</span /> const</span /> int</span /> HTCAPTION = 2</span />;
    protected</span /> override void</span /> WndProc(ref</span /> Message m)
    {
      base.WndProc(ref</span /> m);
      switch</span /> (m.Msg)
      {
        case</span /> WM_NCHITTEST:
          if</span /> (m.Result == (IntPtr</span />)HTCLIENT)
          {
            m.Result = (IntPtr</span />)HTCAPTION;
          }
          break</span />;
      }
    }

If we have 20 forms and if we want them to have the similar behaviour, "copy & paste" the above codes into each one of the 20 files would be nasty. A clean way is to create a derived class from Windows.Form:

public</span /> class</span /> InterceptProcForm:Form

and put the above codes in this class. Then you replace the default base class Form with InterceptProcForm for all your forms:

public</span /> partial class</span /> Form1 : InterceptProcForm 

No more changes in your codes, all the forms are following your mouse now.

However, "ultimate" ends here would be too cheap. The question comes from "what if a form is completely covered by a user control?" Now the message in WndProc() originated from controls will be redirected to the controls' caption which is meaningless to a control and the form will stay there quietly.

The solution is to handle the mousemove event and flood it to its owner form, as shown in this link "C# borderless form move by usercontrol".

Once again we need to take reuse into consideration. In the above link, a generic type FormDragPanel is used and user panels can inherit from it to move the underlying form. It's an improvement but not enough. There are tens if not tons of various types of user control, for some of them we may want the dragging capability and for others we would just keep them as it is. A common type derived from UserControl is not feasible because C# disabled multiple inheritance. A quick thought is to create a separate FormDrag*** for each type of control: FormDragzLabel, FormDragPictureBox, FormDragProgressBar, etc. But your work does not end here. You need to touch the Designer generated code and add event chain in your form for each of the controls.

Inspired by Marcel, a kind of adaptor pattern is adopted to resolve the scenario. Simply add the following class FormDragBase in your project.

public</span /> class</span /> FormDragBase : System.Windows.Forms.Form
    {
        public</span /> const</span /> int</span /> WM_NCLBUTTONDOWN = 0xA1;
        public</span /> const</span /> int</span /> HT_CAPTION = 0x2;
        [DllImportAttribute("</span />user32.dll"</span />)]
        public</span /> static</span /> extern</span /> int</span /> SendMessage
		(IntPtr</span /> hWnd, int</span /> Msg, int</span /> wParam, int</span /> lParam);
        [DllImportAttribute("</span />user32.dll"</span />)]
        public</span /> static</span /> extern</span /> bool</span /> ReleaseCapture();
        protected</span /> override void</span /> OnMouseMove(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if</span /> (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if</span /> (WindowState == FormWindowState.Normal)
                {
                    ReleaseCapture();
                    SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0</span />);
                }
            }
        }
        public</span /> void</span /> AddDraggingControl(System.Windows.Forms.Control theControl)
        {
            theControl.MouseMove += 
		new</span /> System.Windows.Forms.MouseEventHandler(OnControlMouseMove);
        }
        private</span /> void</span /> OnControlMouseMove(object sender, MouseEventArgs e)
        {
            if</span /> (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if</span /> (WindowState == FormWindowState.Normal)
                {
                    ReleaseCapture();
                    SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0</span />);
                }
            }
        }
    }

Derive your own form from this base class, then add the controls for which you want them to be transferable.

public</span /> partial class</span /> Form1 : FormDragBase
    {
        public</span /> Form1()
        {
            InitializeComponent();
            AddDraggingControl(this</span />.label1);
            AddDraggingControl(this</span />.pictureBox1);
            AddDraggingControl(this</span />.progressBar1);
        }
    }

Here you go. Simple and easy!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

trestan
Team Leader
Canada Canada
Looking for something to do in the new year.

Comments and Discussions

 
GeneralNot C++ PinmemberAjay Vijayvargiya26-Nov-10 22:30 
GeneralRe: Not C++ Pinmemberenhzflep26-Nov-10 23:20 
GeneralRe: Not C++ PinmemberAjay Vijayvargiya26-Nov-10 23:22 
GeneralRe: Not C++ PinmemberEmilio Garavaglia27-Nov-10 7:59 
GeneralRe: Not C++ PinmemberAnkur\m/1-Dec-10 0:00 
I don't see it anymore. Smile | :)


..Go Green..

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 26 Nov 2010
Article Copyright 2010 by trestan
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid