无论点击什么元素,都可以使WPF窗口可拖动

时间:2011-09-14 14:09:54

标签: c# wpf user-interface drag-and-drop

我的问题是2倍,我希望 WPF 提供的解决方案更容易,而不是WinForms的标准解决方案(Christophe Geers在我做出此澄清之前提供的解决方案)

首先,有没有办法让Window可拖动而不捕获和处理鼠标点击+拖动事件?我的意思是窗口可以通过标题栏拖动,但如果我设置一个窗口没有一个并且仍然希望能够拖动它,有没有办法只是将事件重新指向任何处理标题栏拖动的处理?

第二,有没有办法将事件处理程序应用于窗口中的所有元素?同样,无论用户点击哪个元素+拖动,都可以使窗口可拖动。显然,没有手动添加处理程序,每个元素。在某个地方做一次吗?

9 个答案:

答案 0 :(得分:247)

当然,请应用MouseDown

的以下Window事件
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ChangedButton == MouseButton.Left)
        this.DragMove();
}

这将允许用户在任何控件上单击/拖动时拖动窗口,除了吃掉MouseDown事件的控件(e.Handled = true

您可以使用PreviewMouseDown代替MouseDown,但是拖动事件会占用Click事件,因此您的窗口会停止响应鼠标左键单击事件。如果您真的希望能够从任何控件单击并拖动窗体,您可以使用PreviewMouseDown,启动计时器以开始拖动操作,并在MouseUp事件触发时取消操作X毫秒。

答案 1 :(得分:7)

如果wpf表单需要可拖动而不管它被点击的位置,那么简单的解决方法就是使用委托来触发windows onload事件或网格加载事件上的DragMove()方法

private void Grid_Loaded(object sender, RoutedEventArgs 
{
      this.MouseDown += delegate{DragMove();};
}

答案 2 :(得分:4)

有时,我们无法访问Window,例如如果我们使用的是DevExpress,那么所有可用的内容都是UIElement

第1步:添加附加属性

解决方案是:

  1. 加入MouseMove事件;
  2. 向上搜索可视树,直到找到第一个父Window;
  3. 在我们新发现的.DragMove()
  4. 上致电Window

    代码:

    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);
            }
        }
    }
    

    步骤2:将附加属性添加到任何元素以使其拖动窗口

    如果我们添加了这个附加属性,用户可以通过单击特定元素来拖动整个窗口:

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

    附录A:可选的高级示例

    DevExpress的示例中,我们用自己的灰色矩形替换停靠窗口的标题栏,然后确保如果用户点击并拖动所述灰色直尺,则窗口将正常拖动:

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

    免责声明:我 DevExpress有关联。此技术适用于任何用户元素,包括standard WPFTelerik(另一个优秀的WPF库提供程序)。

答案 3 :(得分:3)

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

在某些情况下会抛出异常(例如,如果在窗口中您还有一个可点击的图像,单击该图像时会打开一个消息框。当您从消息框中退出时,您将收到错误) 使用

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

所以你确定当时按下了左按钮。

答案 4 :(得分:2)

可以拖拽通过单击表单上的任意位置而不仅仅是标题栏来删除表单。如果你有一个无边框的形式,这很方便。

关于CodeProject的这篇文章演示了一种可能的解决方案来实现这个:

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

基本上创建了Form类型的后代,其中处理了鼠标down,up和move事件。

  • 鼠标按下:记住位置
  • 鼠标移动:存储新位置
  • 鼠标向上:将表单位置移至新位置

以下是视频教程中解释的类似解决方案:

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

当用户点击所述表单中的控件时,我不允许拖动表单。用户点击不同控件时会产生不同的结果。当我的表单突然开始移动因为我点击了一个列表框,按钮,标签......等。这会令人困惑。

答案 5 :(得分:1)

这都是需要的!

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();
        }
    }

答案 6 :(得分:0)

最有用的方法,WPF和Windows窗体,WPF示例:

    [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);
    }

答案 7 :(得分:0)

<Window
...
WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/>
<x:Code>
    <![CDATA[            
        private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }
    ]]>
</x:Code>

source

答案 8 :(得分:0)

@fjch1997所述,实现行为很方便。在这里,核心逻辑与@ loi.efy的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();
        }
    }
}

用法:

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