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

初始想法

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

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

第二种方案

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

  1. public void ViewElemenMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  2. {
  3. var viewElement = sender as ViewElement;
  4. if (viewElemen == null) return;
  5. viewElemen.MouseMove += ViewElemenOnPreviewMouseMove;
  6. }
  7.  
  8. private void PatientTitleOnPreviewMouseMove(object sender, MouseEventArgs mouseEventArgs)
  9. {
  10. var viewElement = sender as ViewElement;
  11. if (viewElement == null) return;
  12. HoloCursor = FormUtility.CreateCursor(viewElement);
  13. viewElemen.RockStart();
  14. viewElemen.GiveFeedback += DragSource_GiveFeedback;
  15. DragDrop.DoDragDrop(viewElemen, Model, DragDropEffects.Copy);
  16. viewElemen.MouseMove -= ViewElemenOnPreviewMouseMove;
  17. viewElemen.GiveFeedback -= DragSource_GiveFeedback;
  18. Mouse.SetCursor(Cursors.Arrow);
  19. }
  20.  
  21. void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
  22. {
  23. var viewElement = sender as ViewElement;
  24. if (viewElemen == null) return;
  25. Mouse.SetCursor(HoloCursor);
  26. e.UseDefaultCursors = false;
  27. e.Handled = true;
  28. }

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

  1. public static Cursor CreateCursor(UIElement element)
  2. {
  3. element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  4. element.Arrange(new Rect(new Point(), element.DesiredSize));
  5.  
  6. var rtb =
  7. new RenderTargetBitmap(
  8. (int)element.DesiredSize.Width,
  9. (int)element.DesiredSize.Height,
  10. 96, 96, PixelFormats.Pbgra32);
  11.  
  12. rtb.Render(element);
  13.  
  14. var encoder = new PngBitmapEncoder();
  15. encoder.Frames.Add(BitmapFrame.Create(rtb));
  16.  
  17. using (var ms = new MemoryStream())
  18. {
  19. encoder.Save(ms);
  20. using (var bmp = new System.Drawing.Bitmap(ms))
  21. {
  22. return InternalCreateCursor(bmp);
  23. }
  24. }
  25. }
  1. private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp)
  2. {
  3. var iconInfo = new NativeMethods.IconInfo();
  4. NativeMethods.GetIconInfo(bmp.GetHicon(), ref iconInfo);
  5.  
  6. iconInfo.xHotspot = 125;
  7. iconInfo.yHotspot = 65;
  8. iconInfo.fIcon = false;
  9.  
  10. SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect(ref iconInfo);
  11. return CursorInteropHelper.Create(cursorHandle);
  12. }
  1. [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
  2. private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid
  3. {
  4. public SafeIconHandle()
  5. : base(true)
  6. {
  7. }
  8.  
  9. override protected bool ReleaseHandle()
  10. {
  11. return NativeMethods.DestroyIcon(handle);
  12. }
  13. }
  1. private static class NativeMethods
  2. {
  3. public struct IconInfo
  4. {
  5. public bool fIcon;
  6. public int xHotspot;
  7. public int yHotspot;
  8. public IntPtr hbmMask;
  9. public IntPtr hbmColor;
  10. }
  11.  
  12. [DllImport("user32.dll")]
  13. public static extern SafeIconHandle CreateIconIndirect(ref IconInfo icon);
  14.  
  15. [DllImport("user32.dll")]
  16. public static extern bool DestroyIcon(IntPtr hIcon);
  17.  
  18. [DllImport("user32.dll")]
  19. [return: MarshalAs(UnmanagedType.Bool)]
  20. public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
  21. }

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

  1. private Storyboard _sb = new Storyboard(){ FillBehavior = FillBehavior.Stop };
  2.  
  3. public ViewElement()
  4. {
  5. InitializeComponent();
  6. _sb.AutoReverse = true;
  7. var dbAscending1 = new DoubleAnimation(0, 3, new Duration(TimeSpan.FromMilliseconds(100)));
  8. _sb.Children.Add(dbAscending1);
  9. Storyboard.SetTarget(dbAscending1, Border);
  10. Storyboard.SetTargetProperty(dbAscending1, new PropertyPath("(Rectangle.RenderTransform).(RotateTransform.Angle)"));
  11. var dbAscending2 = new DoubleAnimation(3, -3, new Duration(TimeSpan.FromMilliseconds(200)));
  12. _sb.Children.Add(dbAscending2);
  13. Storyboard.SetTarget(dbAscending2, Border);
  14. Storyboard.SetTargetProperty(dbAscending2, new PropertyPath("(Rectangle.RenderTransform).(RotateTransform.Angle)"));
  15. }
  16.  
  17. public void RockStart()
  18. {
  19. Dispatcher.InvokeAsync(() => _sb.Begin(), DispatcherPriority.Background);
  20. }

至此,功能完成。

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. UITableViewCell之微博篇

    微博篇 本应用所涉及的知识点: 1.UITableView 中的cell 2.模型的创建 3.MJExtension第三方框架的使用 需求分析 1.界面分析 微博界面 界面控件分析: 整个页面 1.不 ...

  2. maven skip tests

    DskipTests=true is short form of -Dmaven.test.skip=true

  3. NEERC 2013, Eastern subregional contest

    I. The old Padawan Time limit: 0.5 secondMemory limit: 64 MB Yoda: Use the Force. Yes. Now, the ston ...

  4. 翻译:深入 AngularUI Router

    原文地址:http://www.ng-newsletter.com/posts/angular-ui-router.html ui-router: https://angular-ui.github. ...

  5. JAVA集合学习

    JAVA中有几种常用的集合类.分别是List,Set,Map等 提示:Eclipse中自动导入包的快捷键  Ctrl+Shift+O 一.List类 父接口:该类是Collection集合接口的子接口 ...

  6. Runtime机制之结构体及操作函数

    一.动态语言 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理.这种动态语言的优势在于:具有灵活性,比如:消息转发,方法交换等.它有一个运行时系统Ob ...

  7. JS(AS)中的原子操作

    原子操作这是Java多线程编程的老生常谈了.所谓原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程). 当然 ...

  8. 慕课网-安卓工程师初养成-2-10 Java中的强制类型转换

    来源:http://www.imooc.com/code/1241 相信小伙伴们也发现了,尽管自动类型转换是很方便的,但并不能满足所有的编程需要. 例如,当程序中需要将 double 型变量的值赋给一 ...

  9. cocos2d-x Android版游戏之中国移动SDK嵌入

    . 拷贝API 将SDK\runtime\CMBilling20007.jar拷贝至游戏工程的runtime目录下(或其他目录) ,但切记不能放在libs目录下编译,否则编译报错(如:bad rang ...

  10. java springMVC生成二维码

    Zxing是Google提供的工具,提供了二维码的生成与解析的方法,现在使用Java利用Zxing生成二维码 1),二维码的生成 将Zxing-core.jar 包加入到classpath下. 我的下 ...