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));

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

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

    /// <summary>
    /// 当前鼠标点在按钮上的位置
    /// </summary>
    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#后台部分代码
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
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));
    }

    /// <summary>
    /// 区域移动事件
    /// </summary>
    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);
        }
    }

    /// <summary>
    /// 鼠标左键按下
    /// </summary>
    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;
        }
    }

    /// <summary>
    /// 区域鼠标左键抬起
    /// </summary>
    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;
        }
    }
}

四. 原理图

五. 运行效果

六. 工程代码

下载地址

原文地址:https://www.cnblogs.com/Jeffrey-Chou/p/12249596.html

时间: 2025-01-14 13:00:36

WPF实现鼠标拖动控件并带有中间动效的相关文章

WPF 使用鼠标拖动一个控件的实现[2018.7.15]

原文:WPF 使用鼠标拖动一个控件的实现[2018.7.15] Q:已经把一个Shape和一个TextBlock组合起来放到了一个Grid中,现在想要实现用鼠标拖动这个Grid到任意位置的功能,如何做? <Grid Height="50" Width="50"> <Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="50&

WPF,强制捕获鼠标事件,鼠标移出控件外依然可以执行强制捕获的鼠标事件

在WPF中,只有鼠标位置在某个控件上的时候才会触发该控件的鼠标事件.例如,有两个控件都注册了MouseDown和MouseUp事件,在控件1上按下鼠标,不要放开,移动到控件2上再放开.在这个过程中,控件1只会触发MouseDown事件,而控件2则只会触发MouseUp事件,鼠标不在控件上他们就收不到对应的鼠标事件.同样的如果某个控件注册了MouseMove事件,当鼠标移动到控件外之后,控件将不会接收到MouseMove事件.但是在很多情况下我们需要在鼠标移动到控件外之后还能接收鼠标事件.例如按住

WPF Step By Step 控件介绍

WPF Step By Step 控件介绍 回顾 上一篇,我们主要讨论了WPF的几个重点的基本知识的介绍,本篇,我们将会简单的介绍几个基本控件的简单用法,本文会举几个项目中的具体的例子,结合这些 例子,希望我们可以对WPF的掌握会更深刻.本文涉及的内容可能较多.请大家慢慢看看.错误之处,还请指出. 本文大纲 1.基本控件介绍与用法. 基本控件介绍与用法 文本控件 Label控件 label控件:一般用户描述性文字显示. 在Label控件使用时,一般给予用户提示.用法上没有什么很特殊的,label

WPF中的ControlTemplate(控件模板)(转)

原文地址 http://www.cnblogs.com/zhouyinhui/archive/2007/03/28/690993.html WPF中的ControlTemplate(控件模板)                                                                                                                        周银辉 WPF包含数据模板和控件模板,其中控件模板又包括Contro

显示鼠标单击控件次数

#include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); HINSTANCE hInst; /* The 'main' function of Win32 GUI programs: this is where execution starts */ int WINAPI WinMain(     HINSTANCE hInstance,     HINSTANCE hPrevInstance,    

WPF 中动态改变控件模板

在某些项目中,可能需要动态的改变控件的模板,例如软件中可以选择不同的主题,在不同的主题下软件界面.控件的样式都会有所不同,这时即可通过改变控件模板的方式实现期望的功能. 基本方法是当用户点击切换主题按钮是加载新的资源字典,并使用新加载的资源字典替代当前的资源字典这时要用到ResourceManager. 假设现有两个不同的资源字典文件Dictionary1.xaml和Dictionary2.xaml存在于Themes文件夹内: 在MainPage中使用其中一个资源字典作为默认样式文件: <Win

WPF利用通过父控件属性来获得绑定数据源RelativeSource

WPF利用通过父控件属性来获得绑定数据源RelativeSource 有时候我们不确定作为数据源的对象叫什么名字,但知道作为绑定源与UI布局有相对的关系,如下是一段XAML代码,说明多层布局控件中放置一个文本控件,来显示父级控件的名称. 1.XAML Html代码 <Grid x:Name="g1" Background="Red" Margin="10"> <DockPanel x:Name="d1" Ba

高质量的基于向量条形码产生机制 WPF Drawing API条形码控件

Barcode Professional for WPF条形码控件是一款轻量级的 .NET 程序集,为你的WPF程序生成高质量的基于矢量的条码,支持大多数流行的一维和二维条形码:Code 39, Code 128, GS1-128, GS1 DataBar (RSS-14),  EAN 13 & UPC, Postal (USPS, British Royal Mail, Australia Post, DHL, etc.), Data Matrix, QR Code, PDF 417, UPS

Wpf使用Winform控件后Wpf元素被Winform控件遮盖问题的解决

有人会说不建议Wpf中使用Winform控件,有人会说建议使用Winform控件在Wpf下的替代方案,然而在实际工作中由于项目的特殊需求,考虑到时间.成本等因素,往往难免会碰到在WPF中使用Winfrom控件的问题,我们知道Wpf可以通过使用WindowsFormsHost容器调用Winform控件,但是在一些场合需要将Wpf元素显示在Winform控件的上层,此时就会出现Wpf元素被Winform控件遮盖的问题. 一.场景再现 接到公司命令,在时间紧迫的情况下,需要将原来的Winform程序(