使WPF窗口可拖动,无论点击什么元素
我的问题是2折,我希望有更容易的解决scheme,由WPF提供,而不是从WinForms的标准解决scheme(Christophe Geers提供,在我做了这个澄清之前)。
首先,有没有办法使窗口拖动不捕获和处理鼠标点击+拖动事件? 我的意思是窗口是可拖动的标题栏,但如果我设置一个窗口不能有一个,仍然希望能够拖动它,有没有办法只是重新定向事件不知何故处理标题栏拖动?
其次,有没有办法将事件处理程序应用于窗口中的所有元素? 如在,无论用户点击+拖动哪个元素使窗口可拖动。 显然,不需要手动添加处理程序,只需要添加每个元素。 只要在某个地方做一次?
当然,请应用您的Window
的以下MouseDown
事件
private void Window_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) this.DragMove(); }
这将允许用户拖动窗口,当他们点击/拖动任何控制,除了控制吃的MouseDown事件( e.Handled = true
)
您可以使用PreviewMouseDown
而不是MouseDown
,但拖动事件会消耗Click
事件,因此您的窗口将停止响应鼠标左键单击事件。 如果您真的想要能够从任何控件中单击并拖动窗体,则可以使用PreviewMouseDown
,启动计时器以开始拖动操作,并在X毫秒内触发MouseUp
事件时取消操作。
如果wpf表单需要被拖动,无论被点击的地方是什么,简单的解决方法是使用委托来触发窗口onload事件或网格加载事件的DragMove()方法
private void Grid_Loaded(object sender, RoutedEventArgs { this.MouseDown += delegate{DragMove();}; }
private void Window_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) this.DragMove(); }
在某些情况下抛出一个exception(例如,如果在窗口上还有一个可点击的图像,当点击的时候打开一个消息框,当你从消息框中退出时会出错)。
private void Window_MouseDown(object sender, MouseButtonEventArgs e) { if (Mouse.LeftButton == MouseButtonState.Pressed) this.DragMove(); }
所以你确定那个时候按下了左键。
有时,我们无法访问Window
,例如,如果我们使用DevExpress
,所有可用的是UIElement
。
第1步:添加附加的属性
解决scheme是:
- 钩入
MouseMove
事件; - search可视化树,直到find第一个父
Window
; - 在我们新发现的
Window
上调用.DragMove()
。
码:
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的这个例子中,我们用自己的灰色长方形replace了一个对接窗口的标题栏,然后确保如果用户点击并拖动了所述的灰色的rectagle,窗口将正常拖动:
<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 。 这种技术可以与任何用户元素一起工作,包括标准的WPF或Telerik (另一个好的WPF库提供者)。
可以通过单击表单上的任何位置来拖放表单,而不仅仅是标题栏。 如果你有一个无边界的forms,这是很方便的。
这篇关于CodeProject的文章演示了一个可能的解决scheme来实现这个:
http://www.codeproject.com/KB/cs/DraggableForm.aspx
基本上是创build一个表单types的后代,其中处理鼠标向下,向上和移动事件。
- 老鼠下来:记住位置
- 鼠标移动:存储新位置
- 将鼠标移到新的位置
以下是video教程中解释的类似解决scheme:
http://www.youtube.com/watch?v=tJlY9aX73Vs
当用户点击表单中的控件时,我不会允许拖动表单。 当用户点击不同的控件时,epexct会产生不同的结果。 当我的表单突然开始移动,因为我点击了一个列表框,button,标签等等。 这会令人困惑。
最有用的方法,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); }
<Window ... WindowStyle="None" MouseLeftButtonDown="WindowMouseLeftButtonDown"/> <x:Code> <![CDATA[ private void WindowMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DragMove(); } ]]> </x:Code>
资源