WPF实现鼠标拖动控件并带有中间动效

一. 前提

要实现鼠标对控件的拖拽移动,首先必须知道下面几点:

  1. WPF中的鼠标左键按下、鼠标移动事件,有时候通过XAML界面添加的时候并有没有作用;

  2. 如果在移动时候要持续修改控件的属性,我们通过改变RenderTransform来修改呈现,而不是直接修改控件本身的属性(会卡);

  3. 通过 VisualBrush 来填充Rectangle,来实现鼠标拖动控件所形成的影子;

  4. 通过创建一个带有目标依赖属性的Button的子类,来将有关数据放入Button的子类中;

二. 过程

这里以按钮的拖动,分析一下这个过程:

  1. 首先在点击按钮(鼠标左键按下),我们以按钮为原型创建一个 “影子” ;

  2. 在鼠标按住左键拖动的时候,实现对这个 “影子” 的拖动跟随效果;

  3. 最后,在放开鼠标(鼠标左边抬起)时,将原来的按钮的位置直接移动到抬起时的位置并去除跟随的 “影子”;

三. 代码

这边的代码进行了封装,如过要看没有封装的版本请见示例工程(下面可以下载)

  • DragButton 类,继承自 Button 类
public class DragButton : Button
{
    public static readonly DependencyProperty IsDragProperty = DependencyProperty.Register("IsDrag", typeof(Boolean), typeof(DragButton));
    public static readonly DependencyProperty CurrentPosProperty = DependencyProperty.Register("CurrentPos", typeof(Point), typeof(DragButton));
    public static readonly DependencyProperty ClickPosProperty = DependencyProperty.Register("ClickPos", typeof(Point), typeof(DragButton));

    /// 
    /// 是否拖拽
    /// 
    public bool IsDrag
    {
        get
        {
            return (bool)this.GetValue(IsDragProperty);
        }
        set
        {
            this.SetValue(IsDragProperty, value);
        }
    }

    /// 
    /// 按钮的定位位置
    /// 
    public Point CurrentPos
    {
        get
        {
            return (Point)this.GetValue(CurrentPosProperty);
        }
        set
        {
            this.SetValue(CurrentPosProperty, value);
        }
    }

    /// 
    /// 当前鼠标点在按钮上的位置
    /// 
    public Point ClickPos
    {
        get
        {
            return (Point)this.GetValue(ClickPosProperty);
        }
        set
        {
            this.SetValue(ClickPosProperty, value);
        }
    }
}
  • MainWindow的XAML的部分代码
<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,36,9">
        <local:DragButton x:Name="btn" Canvas.Left="173" Canvas.Top="64" Width="80" Height="30" Content="拖拽"/>
    Canvas>
Window>
  • MainWindow的C#后台部分代码
/// 
/// MainWindow.xaml 的交互逻辑
/// 
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        //添加事件
        this.btn.AddHandler(Button.MouseLeftButtonDownEvent, new MouseButtonEventHandler(this.MouseButtonLeftDown), true);
        this.canvas.AddHandler(Canvas.MouseLeftButtonUpEvent, new MouseButtonEventHandler(this.CanvasButtonLeftUp), true);
        this.canvas.AddHandler(Canvas.MouseMoveEvent, new MouseEventHandler(this.Canvas_MouseMove),true);

        this.btn.CurrentPos = new Point((double)this.btn.GetValue(Canvas.LeftProperty), (double)this.btn.GetValue(Canvas.TopProperty));
    }

    /// 
    /// 区域移动事件
    /// 
    private void Canvas_MouseMove(object sender, MouseEventArgs e)
    {
        if(this.btn.IsDrag)
        {
            Point offsetPoint = e.GetPosition(this.canvas);
            double xOffset = offsetPoint.X - this.btn.CurrentPos.X - this.btn.ClickPos.X;
            double yOffset = offsetPoint.Y - this.btn.CurrentPos.Y - this.btn.ClickPos.Y;

            Rectangle rect = LogicalTreeHelper.FindLogicalNode(this, "rect") as Rectangle;
            TranslateTransform transform = (TranslateTransform)rect.RenderTransform;

            transform.X += xOffset;
            transform.Y += yOffset;
            this.btn.CurrentPos = new Point(offsetPoint.X- this.btn.ClickPos.X, offsetPoint.Y- this.btn.ClickPos.Y);
        }
    }

    /// 
    /// 鼠标左键按下
    /// 
    private void MouseButtonLeftDown(object sender, MouseButtonEventArgs e)
    {
        if(!this.btn.IsDrag)
        {
            this.btn.ClickPos = e.GetPosition(this.btn);

            VisualBrush visualBrush = new VisualBrush(this.btn);
            Rectangle rect = new Rectangle() { Width = this.btn.ActualWidth, Height = this.btn.Height, Fill = visualBrush, Name = "rect" };
            rect.SetValue(Canvas.LeftProperty, this.btn.GetValue(Canvas.LeftProperty));
            rect.SetValue(Canvas.TopProperty, this.btn.GetValue(Canvas.TopProperty));
            rect.RenderTransform = new TranslateTransform(0d, 0d);
            rect.Opacity = 0.6;
            this.canvas.Children.Add(rect);

            this.btn.IsDrag = true;
        }
    }

    /// 
    /// 区域鼠标左键抬起
    /// 
    private void CanvasButtonLeftUp(object sender, MouseButtonEventArgs e)
    {
        if (this.btn.IsDrag)
        {
            this.btn.SetValue(Canvas.LeftProperty, this.btn.CurrentPos.X);
            this.btn.SetValue(Canvas.TopProperty, this.btn.CurrentPos.Y);

            Rectangle rect = LogicalTreeHelper.FindLogicalNode(this, "rect") as Rectangle;
            this.canvas.Children.Remove(rect);

            this.btn.IsDrag = false;
        }
    }
}

四. 原理图

WPF实现鼠标拖动控件并带有中间动效_第1张图片

五. 运行效果

WPF实现鼠标拖动控件并带有中间动效_第2张图片

 六. 工程代码

下载地址

你可能感兴趣的:(WPF实现鼠标拖动控件并带有中间动效)