136

My question is 2 fold, and I am hoping there are easier solutions to both provided by WPF rather than the standard solutions from WinForms (which Christophe Geers provided, before I've made this clarification).

First, is there a way to make Window draggable without capturing and processing mouse-click+drag events? I mean the window is draggable by the title bar, but if I set a window not to have one and still want to be able to drag it, is there a way to just re-direct the events somehow to whatever handles the title bar dragging?

Second, is there a way to apply an event handler to all elements in the window? As in, make the window draggable no matter which element the user click+drags. Obviously without adding the handler manually, to every single element. Just do it once somewhere?

Alex K
  • 10,835
  • 8
  • 29
  • 34

10 Answers10

345

Sure, apply the following MouseDown event of your Window

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ChangedButton == MouseButton.Left)
        this.DragMove();
}

This will allow users to drag the Window when they click/drag on any control, EXCEPT for controls which eat the MouseDown event (e.Handled = true)

You can use PreviewMouseDown instead of MouseDown, but the drag event eats the Click event, so your window stops responding to left-mouse click events. If you REALLY wanted to be able to click and drag the form from any control, you could probably use PreviewMouseDown, start a timer to begin the drag operation, and cancel the operation if the MouseUp event fires within X milliseconds.

Rachel
  • 130,264
  • 66
  • 304
  • 490
  • +1. Much better to let the window manager handle the move instead of faking it by remembering position and moving the window. (The latter method also has a tendency to go wrong in certain edge cases, anyway) – Joey Sep 14 '11 at 15:11
  • Why not just set the `MouseLeftButtonDown` event, rather than checking in the .cs? –  Jan 06 '15 at 18:34
  • 2
    @Drowin You could probably use that event instead, but be sure you test it first since `MouseLeftButtonDown` has a direct routing strategy while `MouseDown` has a bubbling routing strategy. See the remarks section of the [MSDN page for MouseLeftButtonDown](http://msdn.microsoft.com/en-us/library/system.windows.uielement.mouseleftbuttondown%28v=vs.110%29.aspx) for more info, and for some extra things to be aware of if you're going to be using `MouseLeftButtonDown` over `MouseDown`. – Rachel Jan 06 '15 at 19:39
  • @Rachel Yeah I'm using it and it works, but thanks for the explanation! –  Jan 06 '15 at 19:40
  • 2
    @Rahul Dragging a UserControl is much harder... you'll need to place it in a parent panel like a Canvas and manually set the X/Y (or Canvas.Top and Canvas.Left) properties as the user moves the mouse. I used mouse events last time I did that, so OnMouseDown capture position & register move event, OnMouseMove change X/Y, and OnMouseUp remove move event. That's the basic idea of it :) – Rachel Jul 24 '15 at 20:36
  • Yes @Rachel i already completed this task by using help of stackoverflow.com/questions/6284056/dragging-a-wpf-user-control link. Anyway thanks for your suggestion. – Rahul Jul 27 '15 at 05:40
  • Lol. For winforms its about 20 lines of code including extern message and included system dlls etc etc. Nice one! Upped this. – C4d Apr 29 '16 at 13:23
  • I remember back to the days of winforms where I copy and pasted like 20 lines here from SO to move the form with drag. 2 minutes ago I thought it would end up the same.... and then I saw your answer.... just LOL. – C4d Jul 13 '16 at 02:35
  • I have a custom modal messagebox which can be located in the parent window at top left, top center, top right, middle left, middle center, middle right, bottom left, bottom center and bottom right but I also wanted to let user to move freely this modal messagebox and your solution works like a charm for me! Just what I was searching for. Thanks! – Willy Nov 11 '17 at 20:42
10

if the wpf form needs to be draggable no matter where it was clicked the easy work around is using a delegate to trigger the DragMove() method on either the windows onload event or the grid load event

private void Grid_Loaded(object sender, RoutedEventArgs 
{
      this.MouseDown += delegate{DragMove();};
}
Pranavan Maru
  • 481
  • 5
  • 10
8

Sometimes, we do not have access to Window, e.g. if we are using DevExpress, all that is available is a UIElement.

Step 1: Add attached property

The solution is to:

  1. Hook into MouseMove events;
  2. Search up the visual tree until we find the first parent Window;
  3. Call .DragMove() on our newly discovered Window.

Code:

using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace DXApplication1.AttachedProperty
{
    public class EnableDragHelper
    {
        public static readonly DependencyProperty EnableDragProperty = DependencyProperty.RegisterAttached(
            "EnableDrag",
            typeof (bool),
            typeof (EnableDragHelper),
            new PropertyMetadata(default(bool), OnLoaded));

        private static void OnLoaded(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var uiElement = dependencyObject as UIElement;
            if (uiElement == null || (dependencyPropertyChangedEventArgs.NewValue is bool) == false)
            {
                return;
            }
            if ((bool)dependencyPropertyChangedEventArgs.NewValue  == true)
            {
                uiElement.MouseMove += UIElementOnMouseMove;
            }
            else
            {
                uiElement.MouseMove -= UIElementOnMouseMove;
            }

        }

        private static void UIElementOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
        {
            var uiElement = sender as UIElement;
            if (uiElement != null)
            {
                if (mouseEventArgs.LeftButton == MouseButtonState.Pressed)
                {
                    DependencyObject parent = uiElement;
                    int avoidInfiniteLoop = 0;
                    // Search up the visual tree to find the first parent window.
                    while ((parent is Window) == false)
                    {
                        parent = VisualTreeHelper.GetParent(parent);
                        avoidInfiniteLoop++;
                        if (avoidInfiniteLoop == 1000)
                        {
                            // Something is wrong - we could not find the parent window.
                            return;
                        }
                    }
                    var window = parent as Window;
                    window.DragMove();
                }
            }
        }

        public static void SetEnableDrag(DependencyObject element, bool value)
        {
            element.SetValue(EnableDragProperty, value);
        }

        public static bool GetEnableDrag(DependencyObject element)
        {
            return (bool)element.GetValue(EnableDragProperty);
        }
    }
}

Step 2: Add Attached Property to any element to let it drag the window

The user can drag the entire window by clicking on a specific element, if we add this attached property:

<Border local:EnableDragHelper.EnableDrag="True">
    <TextBlock Text="Click me to drag this entire window"/>
</Border>

Appendix A: Optional Advanced Example

In this example from DevExpress, we replace the title bar of a docking window with our own grey rectangle, then ensure that if the user clicks and drags said grey rectagle, the window will drag normally:

<dx:DXWindow x:Class="DXApplication1.MainWindow" Title="MainWindow" Height="464" Width="765" 
    xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:dxdo="http://schemas.devexpress.com/winfx/2008/xaml/docking" 
    xmlns:local="clr-namespace:DXApplication1.AttachedProperty"
    xmlns:dxdove="http://schemas.devexpress.com/winfx/2008/xaml/docking/visualelements"
    xmlns:themeKeys="http://schemas.devexpress.com/winfx/2008/xaml/docking/themekeys">

    <dxdo:DockLayoutManager FloatingMode="Desktop">
        <dxdo:DockLayoutManager.FloatGroups>
            <dxdo:FloatGroup FloatLocation="0, 0" FloatSize="179,204" MaxHeight="300" MaxWidth="400" 
                             local:TopmostFloatingGroupHelper.IsTopmostFloatingGroup="True"                             
                             >
                <dxdo:LayoutPanel ShowBorder="True" ShowMaximizeButton="False" ShowCaption="False" ShowCaptionImage="True" 
                                  ShowControlBox="True" ShowExpandButton="True" ShowInDocumentSelector="True" Caption="TradePad General" 
                                  AllowDock="False" AllowHide="False" AllowDrag="True" AllowClose="False"
                                  >
                    <Grid Margin="0">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Border Grid.Row="0" MinHeight="15" Background="#FF515151" Margin="0 0 0 0"
                                                                  local:EnableDragHelper.EnableDrag="True">
                            <TextBlock Margin="4" Text="General" FontWeight="Bold"/>
                        </Border>
                        <TextBlock Margin="5" Grid.Row="1" Text="Hello, world!" />
                    </Grid>
                </dxdo:LayoutPanel>
            </dxdo:FloatGroup>
        </dxdo:DockLayoutManager.FloatGroups>
    </dxdo:DockLayoutManager>
</dx:DXWindow>

Disclaimer: I am not affiliated with DevExpress. This technique will work with any user element, including standard WPF or Telerik (another fine WPF library provider).

Community
  • 1
  • 1
Contango
  • 76,540
  • 58
  • 260
  • 305
6
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
    this.DragMove();
}

Is throwing an exception in some cases (i.e. if on the window you also have a clickable image that when clicked opens a message box. When you exit from message box you will get error) It is safer to use

private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
            this.DragMove();
}

So you are sure that left button is pressed at that moment.

Nick
  • 63
  • 1
  • 3
  • I'm using `e.LeftButton` instead of `Mouse.LeftButton` to specifically use the button associated with the event args, even though it will probably never matter . – Fls'Zen May 11 '15 at 13:25
4

As already mentioned by @fjch1997 it's convenient to implement a behavior. Here it is, the core logic is the same as in the @loi.efy's answer:

public class DragMoveBehavior : Behavior<Window>
{
    protected override void OnAttached()
    {
        AssociatedObject.MouseMove += AssociatedObject_MouseMove;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
    }

    private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed && sender is Window window)
        {
            // In maximum window state case, window will return normal state and
            // continue moving follow cursor
            if (window.WindowState == WindowState.Maximized)
            {
                window.WindowState = WindowState.Normal;

                // 3 or any where you want to set window location after
                // return from maximum state
                Application.Current.MainWindow.Top = 3;
            }

            window.DragMove();
        }
    }
}

Usage:

<Window ...
        xmlns:h="clr-namespace:A.Namespace.Of.DragMoveBehavior"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
    <i:Interaction.Behaviors>
        <h:DragMoveBehavior />
    </i:Interaction.Behaviors>
    ...
</Window>
stop-cran
  • 4,229
  • 2
  • 30
  • 47
3

This is all needed!

private void UiElement_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            if (this.WindowState == WindowState.Maximized) // In maximum window state case, window will return normal state and continue moving follow cursor
            {
                this.WindowState = WindowState.Normal;
                Application.Current.MainWindow.Top = 3;// 3 or any where you want to set window location affter return from maximum state
            }
            this.DragMove();
        }
    }
loi.efy
  • 61
  • 7
2

It is possible to drag & drop a form by clicking anywhere on the form, not just the title bar. This is handy if you have a borderless form.

This article on CodeProject demonstrates one possible solution to implement this:

http://www.codeproject.com/KB/cs/DraggableForm.aspx

Basically a descendant of the Form type is created in which the mouse down, up and move events are handled.

  • Mouse down: remember position
  • Mouse move: store new location
  • Mouse up: position form to new location

And here's a similar solution explained in a video tutorial:

http://www.youtube.com/watch?v=tJlY9aX73Vs

I would not allow dragging the form when a user clicks upon a control in said form. Users epexct different results when they click on different controls. When my form suddenly starts moving because I clicked a listbox, button, label...etc. that would be confusing.

Christophe Geers
  • 8,564
  • 3
  • 37
  • 53
  • Sure it wouldn't move by clicking any control, but if you clicked and dragged, wouldn't you expect the form to move. I mean you wouldn't expect a button or a listbox to move for example if you click+dragged it, motion of the form is a natural expectation if you did try to click and drag a button in the form, I think. – Alex K Sep 14 '11 at 14:44
  • Guess, that's just personal taste. Anyhow ....the controls would need to handle the same mouse events. You would have to notify the parent form of these events as they don't bubble up. – Christophe Geers Sep 14 '11 at 14:46
  • Also, while I was aware of the WinForms solution to this, I was hoping for an easier way to exist in WPF, I guess I should make this clearer in the question (right now it's just a tag). – Alex K Sep 14 '11 at 14:48
  • Sorry, my bad. Did not notice the WPF tag. Wasn't mentioned in the original question. I just assumed WinForms by default, looked over the tag. – Christophe Geers Sep 14 '11 at 14:51
1
<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
<x:Code>
    <![CDATA[            
        private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }
    ]]>
</x:Code>

source

Community
  • 1
  • 1
0

The most usefull method, both for WPF and windows form, WPF example:

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

    public static void StartDrag(Window window)
    {
        WindowInteropHelper helper = new WindowInteropHelper(window);
        SendMessage(helper.Handle, 161, 2, 0);
    }
dexiang
  • 1,345
  • 16
  • 23
0

Add this to your Window style (I think properties are self-explanatory)

<Setter Property="WindowChrome.WindowChrome">
  <Setter.Value>
    <WindowChrome GlassFrameThickness="0" ResizeBorderThickness="3" CornerRadius="0" CaptionHeight="40" />
  </Setter.Value>
</Setter>