使WPF窗口可拖动,无论点击的是哪个元素
原因:有时候我们无法直接访问Window对象,例如使用DevExpress时,只能使用UIElement。所以我们需要找到一个方法来使WPF窗口可拖动,不论点击的是哪个元素。
解决方法:
1. 添加一个附加属性(attached property)
2. 在MouseMove事件中,通过搜索视觉树(visual tree)找到第一个父级Window对象
3. 在这个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); } } }
添加附加属性到任何元素上,使其能够拖动窗口:
附注:该解决方法适用于任何UI元素,包括标准WPF或Telerik等WPF库提供商。
问题的出现原因是需要实现一个可以拖动的WPF窗口,无论点击窗口的哪个元素,都能拖动窗口。解决方法是使用委托在窗口加载事件或者网格加载事件中触发DragMove()方法。可以将以下代码添加到构造函数中来实现:
private void Grid_Loaded(object sender, RoutedEventArgs e) { this.MouseDown += delegate { DragMove(); }; }
然而,如果在窗口的任何位置右键单击,这段代码会抛出异常,因为DragMove()方法只能在按下主鼠标按钮时调用。更好的解决方法是在委托中添加检查ChangedButton的逻辑,只有当鼠标按钮为左键时才调用DragMove()方法:
this.MouseDown += delegate (object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left) DragMove(); };
问题的原因是想要实现在WPF窗口中无论点击哪个元素都能拖动窗口,解决方法是在窗口的MouseDown事件中添加代码来实现拖动窗口的功能。具体的解决方法是在窗口的MouseDown事件中判断点击的鼠标按钮是否是左键,如果是则调用DragMove方法来实现拖动窗口的功能。同时,还可以使用PreviewMouseDown事件代替MouseDown事件,但是需要注意的是,拖动窗口的操作会吞噬掉点击事件,所以窗口将不再响应左键点击事件。如果确实希望能够点击任何控件来拖动窗口,可以使用PreviewMouseDown事件,并在X毫秒内启动计时器来开始拖动操作,如果在这段时间内触发了MouseUp事件,则取消拖动操作。
另外,有人建议优先让窗口管理器处理窗口的移动,而不是自己通过记录位置来移动窗口,因为后一种方法在某些边界情况下容易出错。还某些情况下可以直接设置MouseLeftButtonDown事件来实现拖动窗口的功能,但需要注意的是,MouseLeftButtonDown事件具有直接路由策略,而MouseDown事件具有冒泡路由策略,所以在使用MouseLeftButtonDown代替MouseDown之前要进行测试。此外,还某些情况下对于自定义的用户控件来实现拖动功能会比较困难,需要将用户控件放置在父级面板中,并手动设置X/Y或Canvas.Top和Canvas.Left属性来实现拖动效果。
总结起来,通过在窗口的MouseDown事件中添加代码来实现拖动窗口的功能是解决这个问题的方法。