前一段时间项目里面要实现一个鼠标拖动一个元素到另外一个元素上面并且赋值的功能,由于要在surface上运行,拖动的时候手指会挡住系统默认的拖动图标,导致用户意识不到自己是不是在拖动着东西,所以要解决这个问题。

初始想法

一开始在的设想是,拖动开始时新建一个元素要拖动的元素,然后设置次元素跟随鼠标移动,这里遇到个问题就是,当使用DoDragDrop事件的时候,捕捉不到鼠标的坐标变动,以至于无法让新建的元素跟随移动。要实现功能必须放弃DoDragDrop事件,但是当时很多已经写好的功能都是围绕这个事件的,不想再改,于是开始探索新的方式,虽然一开始浪费了一点时间,但是好处也不是没有,比如发现了GiveFeedback事件,于是就想到了第二种方案。

由于本来就是没有实现的方法,所以在此就不上代码了。

第二种方案

ButtonDown触发的时候,给元素添加MouseMove事件,当MouseMove触发的时候,获取到当前元素并生成图像转换为Cursor格式,在GiveFeedback中用以改变鼠标样式,添加GiveFeedback事件并启动DoDrapDrop。

        public void ViewElemenMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var viewElement = sender as ViewElement;
if (viewElemen == null) return;
viewElemen.MouseMove += ViewElemenOnPreviewMouseMove;
} private void PatientTitleOnPreviewMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
var viewElement = sender as ViewElement;
if (viewElement == null) return;
HoloCursor = FormUtility.CreateCursor(viewElement);
viewElemen.RockStart();
viewElemen.GiveFeedback += DragSource_GiveFeedback;
DragDrop.DoDragDrop(viewElemen, Model, DragDropEffects.Copy);
viewElemen.MouseMove -= ViewElemenOnPreviewMouseMove;
viewElemen.GiveFeedback -= DragSource_GiveFeedback;
Mouse.SetCursor(Cursors.Arrow);
} void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
var viewElement = sender as ViewElement;
if (viewElemen == null) return;
Mouse.SetCursor(HoloCursor);
e.UseDefaultCursors = false;
e.Handled = true;
}

之所以把这么麻烦是因为,鼠标点击事件还要有其他的业务操作,所以只能加在mousemove里面。
根据元素生成鼠标 FormUtility.CreateCursor:

        public static Cursor CreateCursor(UIElement element)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(new Point(), element.DesiredSize)); var rtb =
new RenderTargetBitmap(
(int)element.DesiredSize.Width,
(int)element.DesiredSize.Height,
96, 96, PixelFormats.Pbgra32); rtb.Render(element); var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb)); using (var ms = new MemoryStream())
{
encoder.Save(ms);
using (var bmp = new System.Drawing.Bitmap(ms))
{
return InternalCreateCursor(bmp);
}
}
}
        private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp)
{
var iconInfo = new NativeMethods.IconInfo();
NativeMethods.GetIconInfo(bmp.GetHicon(), ref iconInfo); iconInfo.xHotspot = 125;
iconInfo.yHotspot = 65;
iconInfo.fIcon = false; SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect(ref iconInfo);
return CursorInteropHelper.Create(cursorHandle);
}
       [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeIconHandle()
: base(true)
{
} override protected bool ReleaseHandle()
{
return NativeMethods.DestroyIcon(handle);
}
}
        private static class NativeMethods
{
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
} [DllImport("user32.dll")]
public static extern SafeIconHandle CreateIconIndirect(ref IconInfo icon); [DllImport("user32.dll")]
public static extern bool DestroyIcon(IntPtr hIcon); [DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
}

RockStart是一个元素效果,即开始拖动的时候,该元素会左右摇晃,让用户更清楚的知道,自己拖动的是哪个元素。

        private Storyboard _sb = new Storyboard(){ FillBehavior = FillBehavior.Stop };

        public ViewElement()
{
InitializeComponent();
_sb.AutoReverse = true;
var dbAscending1 = new DoubleAnimation(0, 3, new Duration(TimeSpan.FromMilliseconds(100)));
_sb.Children.Add(dbAscending1);
Storyboard.SetTarget(dbAscending1, Border);
Storyboard.SetTargetProperty(dbAscending1, new PropertyPath("(Rectangle.RenderTransform).(RotateTransform.Angle)"));
var dbAscending2 = new DoubleAnimation(3, -3, new Duration(TimeSpan.FromMilliseconds(200)));
_sb.Children.Add(dbAscending2);
Storyboard.SetTarget(dbAscending2, Border);
Storyboard.SetTargetProperty(dbAscending2, new PropertyPath("(Rectangle.RenderTransform).(RotateTransform.Angle)"));
} public void RockStart()
{
Dispatcher.InvokeAsync(() => _sb.Begin(), DispatcherPriority.Background);
}

至此,功能完成。

WPF DragDrop事件元素跟随的更多相关文章

  1. WPF 在事件中绑定命令(不可以在模版中绑定命令)

    其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实现将命令绑定到事件中. 上一篇中我们介绍了MVVMLight中的命令的用法,那么仅仅知道命令是 ...

  2. WPF自学入门(三)WPF路由事件之内置路由事件

    有没有想过在.NET中已经有了事件机制,为什么在WPF中不直接使用.NET事件要加入路由事件来取代事件呢?最直观的原因就是典型的WPF应用程序使用很多元素关联和组合起来,是否还记得在WPF自学入门(一 ...

  3. WPF路由事件二:路由事件的三种策略

    一.什么是路由事件 路由事件是一种可以针对元素树中的多个侦听器而不是仅仅针对引发该事件的对象调用处理程序的事件.路由事件是一个CLR事件. 路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件 ...

  4. WPF 在事件中绑定命令

    导航:MVVMLight系列文章目录:<关于 MVVMLight 设计模式系列> 其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实 ...

  5. js进阶 12-3 如何实现元素跟随鼠标移动

    js进阶 12-3 如何实现元素跟随鼠标移动 一.总结 一句话总结:获取鼠标位置,将鼠标位置设置为元素偏移即可. 1.用什么事件获取鼠标位置? 用mousemove可以获取鼠标移动的时候的位置 $(d ...

  6. WPF 路由事件 Event Routing

    原文:WPF 路由事件 Event Routing 1.路由事件介绍 之前介绍了WPF的新的依赖属性系统,本篇将介绍更高级的路由事件,替换了之前的.net普通事件.相比.net的事件,路由事件具有更强 ...

  7. Win7/Win8/Win8.1/Win10下的DragEnter DragDrop事件不触发

    Win7/Win8/Win8.1/Win10下的DragDrop事件不触发 2011-02-02  来自:博客园  字体大小:[大 中 小] 摘要:你的应用程序需要从windows资源管理器拖动文件到 ...

  8. Win7下的DragEnter、DragDrop事件不触发的解决方案

    Win7与原来的XP和Win2003相比,安全控制方面更严格.比如,当我们以administrator登陆XP或Win2003时,运行所有的程序即是以管理员的身份启动的.但当以administrato ...

  9. javascript元素跟随鼠标在指定区域运动

    元素跟随鼠标在指定区域运动通常是用在商城图片的放大镜中,下面是完整的Demo: <!DOCTYPE html> <html lang="en"> <h ...

随机推荐

  1. ibatis cannot find <sql> pattern

    "Could not find SQL statement to include with refid xxx"A: 认真把每个用到xxx的地方都看一遍

  2. H264 Profile

    H.264有四种profile,对应四种H264画质级别.每个profile支持一组特定的编码功能,并支持一类特定的应用,分别是BP.EP.MP.HP: 1. BP-Baseline Profile ...

  3. Oracle中MD5+Base64加密实现

    JAVA实现: public static String getMD5(String str) throws Exception {   MessageDigest md5 = MessageDige ...

  4. 重设mysql的root密码,MAC OSX

    前些天装得mysql突然链接不上了,原谅我小白,没有在安装后改密码,mysql初始登陆不需要密码,但是之后root是有个临时密码的,然后..然后就登不上了. 网上有很多改密码的帖子,关键的UPDATE ...

  5. python连mysql时避免出现乱码

    使用python连mysql时候,常常出现乱码,采取以下措施可以避免 1 Python文件设置编码 utf-8 (文件前面加上 #encoding=utf-8)2 MySQL数据库charset=ut ...

  6. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part5:右值引用就是右值吗?

    本文为第五部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...

  7. Docker学习总结之docker介绍

    About Docker 以下均翻译自Docker官方文档 ,转载请注明:Vikings翻译 Develop, Ship and Run Any Application, Anywhere Docke ...

  8. MVC session过期如何处理跳转

    以前我们总是会写一个基类也叫父类来判断session是否已过期然后跳转到指定的错误页面或者登陆界面,然后让所有的页面都继承这个基类,但是当我们应用到MVC项目中时,发现该方法并不会起作用.这时我们可以 ...

  9. 吉布斯现象( Gibbs)

    在连续傅里叶级数(或积分)变换中,信号所对应的离散频谱(或连续频谱)为(或),其频率是无限离散分布的(或频谱的分布范围是无限区间的).很显然,单位时间内,频率较低(简称低频,即较小)的简谐波相对频率较 ...

  10. MySQL的体系结构

    因为MySQL采用的是客户机/服务器体系结构,所以你在使用MySQL存取数据时,必须至少使用两个或者说两类程序: 一个位于存放您的数据的主机上的程序 ----数据库服务器.数据库服务器监听从网络上传过 ...