直观:WPF实现具有中等动态效果的鼠标拖动控制

电脑杂谈  发布时间:2020-10-20 06:02:56  来源:网络整理

wpf 鼠标拖动控件_wpf 鼠标拖动控件

一.前提

要实现鼠标到控件的拖动运动,必须首先知道以下几点:

通过XAML界面添加时,WPF中的鼠标左键按下和鼠标移动事件有时不起作用。我们需要通过绑定触发事件的元素和要监视的路由事件来手动触发事件;

如果要在移动时连续修改控件的属性,我们将通过更改RenderTransform来修改呈现,而不是直接修改控件本身的属性(将被卡住);

使用VisualBrush填充矩形以实现鼠标拖动控件形成的阴影;

通过创建具有目标依赖属性的Button子类,将相关数据放入Button的子类中;

不必通过UIElement.CaptureMouse()和UIElement.ReleaseMouseCapture()捕获并释放鼠标

wpf 鼠标拖动控件_wpf 鼠标拖动控件

屏蔽一些键盘热键,这些热键在抬起鼠标时会导致消息丢失,例如:Alt + Ctrl + A,屏幕截图等热键;

二.处理

我们将按钮拖动到此处以分析过程:

首先单击按钮(按鼠标左键),我们以该按钮为原型创建“阴影”;

在按住鼠标左键的同时拖动鼠标,可以实现该“阴影”的拖动和跟随效果;

最后,当释放鼠标(抬起鼠标的左侧)时,原始按钮位置将直接移动到抬起按钮的位置,并删除以下“阴影”;

三.代码

wpf 鼠标拖动控件_wpf 鼠标拖动控件

此处的代码已打包,如果您想查看未打包的版本,请参阅示例项目(可从下面下载)

/// 
/// 拖拽按钮
/// 
public class DragButton : Button
{
    //依赖属性
    private static readonly DependencyProperty IsDragProperty = DependencyProperty.Register("IsDrag", typeof(Boolean), typeof(DragButton));
    private static readonly DependencyProperty CurrentPosProperty = DependencyProperty.Register("CurrentPos", typeof(Point), typeof(DragButton));
    private static readonly DependencyProperty ClickPosProperty = DependencyProperty.Register("ClickPos", typeof(Point), typeof(DragButton));
    private static readonly DependencyProperty RectProperty = DependencyProperty.Register("Rect", typeof(Rectangle), typeof(DragButton));
    /// 
    /// 是否拖拽
    /// 
    public bool IsDrag
    {
        get
        {
            return (bool)this.GetValue(IsDragProperty);
        }
        set
        {
            this.SetValue(IsDragProperty, value);
        }
    }
    /// 
    /// 按钮的定位位置
    /// 按钮左上角的位置
    /// 
    public Point CurrentPos
    {
        get
        {
            //第一次获取如果是没有被初始化,那么吧按钮的坐标初始化过来
            Point p = (Point)this.GetValue(CurrentPosProperty);
            if (p.X == 0 && p.Y == 0)
            {
                p.X = Canvas.GetLeft(this);
                p.Y = Canvas.GetTop(this);
            }
            return p;
        }
        set
        {
            this.SetValue(CurrentPosProperty, value);
        }
    }
    /// 
    /// 当前鼠标点在按钮上的位置
    /// 
    public Point ClickPos
    {
        get
        {
            return (Point)this.GetValue(ClickPosProperty);
        }
        set
        {
            this.SetValue(ClickPosProperty, value);
        }
    }
    /// 
    /// 虚拟出来的按钮的显示矩形
    /// 
    public Rectangle Rect
    {
        get
        {
            if (this.GetValue(RectProperty) == null)
            {
                //创建VisualBrush
                VisualBrush visualBrush = new VisualBrush(this);
                Rectangle rect = new Rectangle() { Width = this.ActualWidth, Height = this.ActualHeight, Fill = visualBrush, Name = "rect" };
                //设置值
                Canvas.SetLeft(rect, Canvas.GetLeft(this));
                Canvas.SetTop(rect, Canvas.GetTop(this));
                rect.RenderTransform = new TranslateTransform(0d, 0d);
                rect.Opacity = 0.6;
                this.SetValue(RectProperty, rect);
            }
            return (Rectangle)this.GetValue(RectProperty);
        }
    }
}

<Window x:Class="Demo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Demo"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        x:Name="mainWindow">
    <Canvas x:Name="canvas" Background="Aqua" Margin="0,0,0,0">
        <local:DragButton x:Name="btn" Canvas.Left="173" Canvas.Top="64" Width="80" Height="30" Content="拖拽"/>
        <local:DragButton x:Name="btn1" Canvas.Left="94" Canvas.Top="166" Width="80" Height="30" Content="拖拽"/>
    Canvas>
Window>

/// 
/// MainWindow.xaml 的交互逻辑
/// 
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        //添加事件
        this.btn.AddHandler(Canvas.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.MouseButtonLeftDown), true);
        this.btn1.AddHandler(Canvas.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.MouseButtonLeftDown), true);
        //防止一些热键的影响
        this.AddHandler(Window.KeyDownEvent, new RoutedEventHandler(this.OtherKeyDownEvent), true);
    }
    /// 
    /// 区域移动事件
    /// 
    private void Canvas_MouseMove(object sender, MouseEventArgs e)
    {
        DragButton dragButton = sender as DragButton;
        if (dragButton != null && dragButton.IsDrag)
        {
            Point offsetPoint = e.GetPosition(this.canvas);
            double xOffset = offsetPoint.X - dragButton.CurrentPos.X - dragButton.ClickPos.X;
            double yOffset = offsetPoint.Y - dragButton.CurrentPos.Y - dragButton.ClickPos.Y;
            TranslateTransform transform = (TranslateTransform)dragButton.Rect.RenderTransform;
            transform.X += xOffset;
            transform.Y += yOffset;
            dragButton.CurrentPos = new Point(offsetPoint.X - dragButton.ClickPos.X, offsetPoint.Y - dragButton.ClickPos.Y);
        }
    }
    /// 
    /// 鼠标左键按下
    /// 
    private void MouseButtonLeftDown(object sender, MouseButtonEventArgs e)
    {
        DragButton dragButton = sender as DragButton;
        if (dragButton != null && !dragButton.IsDrag)
        {
            dragButton.ClickPos = e.GetPosition(dragButton);
            this.canvas.Children.Add(dragButton.Rect);
            dragButton.IsDrag = true;
            //注册事件
            dragButton.AddHandler(Canvas.MouseMoveEvent, new MouseEventHandler(this.Canvas_MouseMove), true);
            dragButton.AddHandler(Canvas.MouseLeftButtonUpEvent, new MouseButtonEventHandler(this.CanvasButtonLeftUp), true);
        }
    }
    /// 
    /// 区域鼠标左键抬起
    /// 
    private void CanvasButtonLeftUp(object sender, MouseButtonEventArgs e)
    {
        ReducingButton(sender);
    }
    /// 
    /// 防止一些热键的影响
    /// 
    private void OtherKeyDownEvent(object sender, RoutedEventArgs e)
    {
        ReducingButton(sender);
    }
    /// 
    /// 避免重复代码
    /// 
    private void ReducingButton(object sender)
    {
        DragButton dragButton = sender as DragButton;
        if (dragButton != null && dragButton.IsDrag)
        {
            Canvas.SetLeft(dragButton, dragButton.CurrentPos.X);
            Canvas.SetTop(dragButton, dragButton.CurrentPos.Y);
            this.canvas.Children.Remove(dragButton.Rect);
            dragButton.IsDrag = false;
            //移除事件
            dragButton.RemoveHandler(Canvas.MouseMoveEvent, new MouseEventHandler(this.Canvas_MouseMove));
            dragButton.RemoveHandler(Canvas.MouseLeftButtonUpEvent, new MouseButtonEventHandler(this.CanvasButtonLeftUp));
        }
    }
}

四.原理图

wpf 鼠标拖动控件

五.跑步效果

wpf 鼠标拖动控件_wpf 鼠标拖动控件

wpf 鼠标拖动控件

六.项目代码

下载URL

七.一些补充

关于这一点的内容后来为我自己阅读了之前的代码,然后进行了修改和添加,并认为这不好。总共编写了4个版本,并且每个版本都在先前版本上进行了优化。最终版本是名称后带有“最终版本”的版本。

在这里记笔记

1.关于路由事件的绑定,当我之前阅读本书时,并不清楚。首先,“ UIElement.AddHandler”侧的UIElement将成为事件xxxHandler的发送者对象,触发该事件通过的路由的触发者必须通过e.Source或e.OriginalSource获得。简而言之,指定要处理的元素。通过树上的指定路由事件来传递要处理的内容;

wpf 鼠标拖动控件_wpf 鼠标拖动控件

2.将元素设置为Canvas子类的左边缘时,请使用:

Canvas.SetLeft(UIElement,double);

在设置时使用:

Canvas.GetLeft(UIElement);

而不是通过以下方式进行设置/获取:

UIElement.SetValue(DependencyProperty,object);
UIElement.GetValue(DependencyProperty);

注意:上面的方法可以,但是编写起来比较麻烦。我们必须充分利用附加属性的特征。通常,附加属性是在附加属性所在的对象上设置的,而不是在附加对象上设置的。例如,要向Person添加School的学校班级的附加属性,则该学校中应存在设置班级的附加属性的方法。因此,这与直接调用School方法向人。

3.路由事件可以延迟绑定,无需在开始时声明;

4.UIElement.CaptureMouse()和UIElement.ReleaseMouseCapture()是否应让元素捕获鼠标以防止某些特殊错误,这取决于情况。在这里,每当我们要使用鼠标单击或拖动时,都必须考虑此问题。

5.为了填充Canvas和其他元素,可以使用Margin =“ 0,0,0,0”来实现;


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/bofangqi/article-327302-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...