使WPF窗口可拖动,无论点击的是哪个元素

9 浏览
0 Comments

使WPF窗口可拖动,无论点击的是哪个元素

我的问题是两个方面的,希望WPF可以提供比WinForms更简单的解决方案(在我澄清之前,Christophe Geers提供了标准解决方案)。\n首先,是否有一种方法可以使窗口可拖动,而无需捕获和处理鼠标点击+拖动事件?我的意思是,窗口可以通过标题栏拖动,但如果我设置一个窗口没有标题栏,仍然想能够拖动它,是否有一种方法可以将事件重定向到处理标题栏拖动的地方?\n第二,是否有一种方法可以将事件处理程序应用于窗口中的所有元素?也就是说,使窗口中的任何元素都可以被用户点击+拖动。显然,不需要手动为每个元素添加处理程序。只需在某个地方执行一次即可。

0
0 Comments

原因:有时候我们无法直接访问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库提供商。

0
0 Comments

问题的出现原因是需要实现一个可以拖动的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(); 
};

0
0 Comments

问题的原因是想要实现在WPF窗口中无论点击哪个元素都能拖动窗口,解决方法是在窗口的MouseDown事件中添加代码来实现拖动窗口的功能。具体的解决方法是在窗口的MouseDown事件中判断点击的鼠标按钮是否是左键,如果是则调用DragMove方法来实现拖动窗口的功能。同时,还可以使用PreviewMouseDown事件代替MouseDown事件,但是需要注意的是,拖动窗口的操作会吞噬掉点击事件,所以窗口将不再响应左键点击事件。如果确实希望能够点击任何控件来拖动窗口,可以使用PreviewMouseDown事件,并在X毫秒内启动计时器来开始拖动操作,如果在这段时间内触发了MouseUp事件,则取消拖动操作。

另外,有人建议优先让窗口管理器处理窗口的移动,而不是自己通过记录位置来移动窗口,因为后一种方法在某些边界情况下容易出错。还某些情况下可以直接设置MouseLeftButtonDown事件来实现拖动窗口的功能,但需要注意的是,MouseLeftButtonDown事件具有直接路由策略,而MouseDown事件具有冒泡路由策略,所以在使用MouseLeftButtonDown代替MouseDown之前要进行测试。此外,还某些情况下对于自定义的用户控件来实现拖动功能会比较困难,需要将用户控件放置在父级面板中,并手动设置X/Y或Canvas.Top和Canvas.Left属性来实现拖动效果。

总结起来,通过在窗口的MouseDown事件中添加代码来实现拖动窗口的功能是解决这个问题的方法。

0