Make WPF window draggable, no matter what element is clicked

前端 未结 9 994
灰色年华
灰色年华 2020-12-04 07:47

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

相关标签:
9条回答
  • 2020-12-04 08:09
    <Window
    ...
    WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
    <x:Code>
        <![CDATA[            
            private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                DragMove();
            }
        ]]>
    </x:Code>
    

    source

    0 讨论(0)
  • 2020-12-04 08:14

    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).

    0 讨论(0)
  • 2020-12-04 08:15
    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.

    0 讨论(0)
  • 2020-12-04 08:20

    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.

    0 讨论(0)
  • 2020-12-04 08:22

    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);
        }
    
    0 讨论(0)
  • 2020-12-04 08:27

    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();};
    }
    
    0 讨论(0)
提交回复
热议问题