我需要窗口内的某个元素拥有拖动整个窗口的功能,也就是这个元素在拖动的时候是拖动整个窗口。而且我还开出一个有趣的方法,这个作为窗口的拖拽的元素如果是用户在元素上拖动,那么将会拖动窗口,如果用户是点击,将会触发点击事件

附加属性可以给某个元素附加有趣的功能,本文的功能需要拖动元素的时候实际上是拖动窗口,第二个是元素是支持点击的

拖动窗口使用的是窗口的 DragMove 方法

元素支持点击用的是 WPF 给任意控件通过按下移动抬起封装点击事件 方法

因此本文需要引入 WPF 给任意控件通过按下移动抬起封装点击事件 的 InputHelper 类作为辅助

用法如下,写一个简单的界面,放一个元素作为拖动的元素

<Border x:Name="DraggingElement" Width="100" Height="100" Background="Gray">
 

    <TextBlock x:Name="TextBlock" Margin="10,10,10,10" HorizontalAlignment="Center"></TextBlock>
</Border>

上面代码的 DraggingElement 就是用来拖动窗口的元素

接下来在元素放一个 WindowDraggingExtension.DragWindow 附加属性

<Border x:Name="DraggingElement" Width="100" Height="100" Background="Gray">

   <framework:WindowDraggingExtension.DragWindow>
        <framework:WindowDraggingExtension />
    </framework:WindowDraggingExtension.DragWindow> 

    <TextBlock x:Name="TextBlock" Margin="10,10,10,10" HorizontalAlignment="Center"></TextBlock>
</Border>

注意 framework: 是我的命名空间,请按照自己的代码修改为你的命名空间

此时尝试运行代码,拖动一下 DraggingElement 这个元素,可以看到拖动的是窗口。这个方法支持触摸拖动

这个附加属性能做到的功能类似 QQ 宠物,可以拖动,可以点击提示更多内容

本文用到的这个附加属性代码如下

    /// <summary>
    /// 窗口拖拽的附加方法
    /// </summary>
    public class WindowDraggingExtension
    {
        /// <summary>
        /// 表示元素作为附加某个窗口提供拖拽的功能
        /// </summary>
        public static readonly DependencyProperty DragWindowProperty = DependencyProperty.RegisterAttached(
            "DragWindow", typeof(WindowDraggingExtension), typeof(WindowDraggingExtension),
            new PropertyMetadata(default(WindowDraggingExtension),
                OnDragWindowPropertyChanged));

        /// <summary>
        /// 附加的拖动的窗口,提供此属性仅仅是为了提升性能,可以不设置。如不设置将使用 Window.GetWindow 方法获取当前元素所在窗口
        /// </summary>
        public Window TargetWindow { set; get; }

        /// <summary>
        /// 拖动的元素实际是被点击时触发
        /// </summary>
        public event EventHandler DraggingElementClicked;

        /// <summary>
        /// 拖动时触发
        /// </summary>
        public event EventHandler Dragging;

        /// <summary>
        /// 设置元素作为窗口的拖拽元素
        /// </summary>
        /// <param name="element"></param>
        /// <param name="value"></param>
        public static void SetDragWindow(DependencyObject element, WindowDraggingExtension value)
        {
            element.SetValue(DragWindowProperty, value);
        }

        /// <summary>
        /// 获取元素作为窗口拖拽属性
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public static WindowDraggingExtension GetDragWindow(DependencyObject element)
        {
            return (WindowDraggingExtension) element.GetValue(DragWindowProperty);
        }

        private static void OnDragWindowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // 仅有设置,不会存在多次设置,也没有反过来
            if (e.NewValue is WindowDraggingExtension windowDragging && d is UIElement element)
            {
                InputHelper.AttachMouseDownMoveUpToClick(element,
                    delegate { windowDragging.OnDraggingElementClicked(); }, delegate
                    {
                        windowDragging.OnDragging();

                        if (Mouse.LeftButton == MouseButtonState.Pressed)
                        {
                            var targetWindow = windowDragging.TargetWindow
                                               ?? Window.GetWindow(element);

                            targetWindow?.DragMove();
                        }
                    });
            }
        }

        private void OnDraggingElementClicked()
        {
            DraggingElementClicked?.Invoke(this, EventArgs.Empty);
        }

        private void OnDragging()
        {
            Dragging?.Invoke(this, EventArgs.Empty);
        }
    }

上面代码 InputHelper 需要从 WPF 给任意控件通过按下移动抬起封装点击事件 复制

通过阅读上面代码,可以看到还有两个可以设置的属性,一个是 TargetWindow 属性,一个是元素被点击的事件

设置 TargetWindow 属性主要是为了提升一点性能,通过 TargetWindow 获取窗口,而不需要通过 Window.GetWindow 方法获取当前元素所在窗口,使用方法如下

<Window x:Name="CurrentWindow" 忽略元素>

  <Border x:Name="DraggingElement" Width="100" Height="100" Background="Gray">

     <framework:WindowDraggingExtension.DragWindow>
          <framework:WindowDraggingExtension TargetWindow="{x:Reference CurrentWindow}" DraggingElementClicked="WindowDraggingExtension_OnDraggingElementClicked"/>
      </framework:WindowDraggingExtension.DragWindow> 

      <TextBlock x:Name="TextBlock" Margin="10,10,10,10" HorizontalAlignment="Center"></TextBlock>
  </Border>
</Window>

上面代码还使用方法拿到元素点击的事件,后台代码如下

        private void WindowDraggingExtension_OnDraggingElementClicked(object sender, EventArgs e)
        {
            TextBlock.Text = "林德熙是逗比";
        }

本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/WPF-%E9%99%84%E5%8A%A0%E5%B1%9E%E6%80%A7%E6%8F%90%E4%BE%9B%E6%9F%90%E4%B8%AA%E5%85%83%E7%B4%A0%E6%8B%A5%E6%9C%89%E6%8B%96%E6%8B%BD%E7%AA%97%E5%8F%A3%E7%9A%84%E5%8A%9F%E8%83%BD.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 lindexi (包含链接: https://dotnet-campus.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系