写在前面:本文为即兴而作,因此难免有疏漏和词不达意的地方。在这里,非常期望您提供评论,分享您的想法和建议。

  这是一篇介绍如何在WPF中实现拖放功能的短文。

  首先要读者清楚的一件事情是:拖放主要分为拖放源和拖放目标两个组成。拖放源和拖放目标各自拥有不同的事件。软件开发人员需要在适当的事件中完成相应功能。

  试想拖放是如何操作的:用户选中一个界面元素,并在鼠标左键按下的情况下移动鼠标,最后,在到达拖放目标时松开鼠标左键,从而完成数据拖放的全过程。从程序编写的角度来看,用户需要在左键选中项目并按下的情况下移动以启动拖放,并在鼠标移动的过程中给出当前拖放状态的外观回馈,并在松开鼠标时尝试将项目添加到目标中。

  对于任意的软件界面,鼠标左键按下并移动的行为并不一定会导致拖放的开始。因此软件开发人员需要自行编写代码启动拖放功能,而不是由WPF决定。这也就是软件开发人员在特定条件下需要自行调用DragDrop.DoDragDrop()以启动拖放操作的原因。DragDrop.DoDragDrop()函数接受三个参数:dragSource、data以及allowedEffects。特别需要注意的是dragSource参数。该参数标示了拖拽操作的消息源,也决定了所有的消息源事件由谁发出。参数data则用来包装Drag&Drop所操作的数据。一般情况下,其都是一个DataObject类型的实例。该实例内部应包装拖拽所实际操作的数据。最后,allowedEffects可以用来指定拖拽操作的效果。调用该函数的片断可以如下所示:

DragDrop.DoDragDrop(mListBox, dataObject, DragDropEffects.Copy);
 

  启动了拖拽操作以后,软件开发人员就需要处理拖拽过程中所发生的一系列事件了。在这种情况下,软件开发人员不能寄望于通过响应Mouse.MouseMove等事件完成拖拽行为的响应。这是因为DragDrop.DoDragDrop()函数实际上是一个阻塞函数。在拖拽行为终止之前,这些事件都不会被发送。取而代之的是,软件开发人员可以使用拖拽源和目标所提供的事件。拖拽源提供的事件为QueryContinueDrag、GiveFeedback以及对应的Preview-事件。QueryContinueDrag事件用来决定是否继续拖放操作。该事件发生的时机为键盘或鼠标按钮状态发生变化时。GiveFeedback则用来为用户提供拖放的反馈信息,如令被拖拽的界面元素的图像随鼠标变化,或提供一个Tooltip提示用户当前的拖放效果等。该消息会在拖拽过程中定时发出,因此软件开发人员也可以将其作为Timer事件使用。在这里强调一下,因为在拖拽过程中软件开发人员不能使用其它事件,因此了解各拖拽事件发生时机是一个很重要的事情。

  另外,在您看到Preview-事件的时候,相信您一定能够想到这是一个隧道/冒泡路由事件。因此,对这些事件的处理并不一定需要向拖放源添加事件处理函数,而可以在较高层次中重写相应函数即可,如重写Window的OnGiveFeedback()函数。这样做的好处在于,其能提供较为集中的拖拽处理逻辑,并在需要更改较高层次界面元素,如窗口的状态栏状态时拥有较好的语义特征。这样做的不足之处也有,如其并不太适合窗口中拥有多个拖拽源的情况。当然,在高层次侦听消息源发出的消息也是非常好的选择。

  如果我们需要实现在拖拽过程中将拖拽的界面元素的样子作为预览这一功能,那么该功能的实现如下所示:

mListBox.PreviewMouseMove += OnPreviewListBoxMouseMove;
mListBox.QueryContinueDrag += OnQueryContinueDrag; private void OnQueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
mAdornerLayer.Update();

} private void OnPreviewListBoxMouseMove(object sender, MouseEventArgs e)
{
ListBoxItem listBoxItem = … // Find your actual visual you want to drag

DragDropAdorner adorner = new DragDropAdorner(listBoxItem);
mAdornerLayer = AdornerLayer.GetAdornerLayer(mTopLevelGrid);
mAdornerLayer.Add(adorner); DataItem dataItem = listBoxItem.Content as DataItem;
DataObject dataObject = new DataObject(dataItem.Clone());
// Here, we should notice that dragsource param will specify on which
// control the drag&drop event will be fired
System.Windows.DragDrop.DoDragDrop(mListBox, dataObject, DragDropEffects.Copy);

}
 

  其中DragDropAdorner用来显示被拖拽的界面元素的预览。其使用了Adorner。如果读者对该控件的实现有兴趣,请自行下载示例程序查看。有关Adorner的使用,我会在有时间的时候专门撰写一篇文章。

  接下来就是拖拽目标事件了。拖拽目标会发送DragEnter、DragOver、DragLeave、Drop以及相应的Preview-事件。这些事件的意义十分清晰,相信您从名字中就能看出这些事件发生的时机。在这些事件中需要注意的则是传入的DragEventArgs。通过设置它的Effects成员,软件开发人员可以控制鼠标的状态,以提示用户当前拖拽动作的光标反馈。同时通过它的Data属性,软件开发人员可以获得DoDragDrop()函数调用时所传入的数据。

  下面就是一段响应拖拽目标事件的代码:

private void OnDragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.None; // Find the corresponding treeview item in mTreeView and select it
Point pos = e.GetPosition(mTreeView);
HitTestResult result = VisualTreeHelper.HitTest(mTreeView, pos);
if (result == null)
return; TreeViewItem selectedItem = Utils.FindVisualParent<TreeViewItem>(result.VisualHit);
if (selectedItem != null)
selectedItem.IsSelected = true; e.Effects = DragDropEffects.Copy;
} private void OnDrop(object sender, DragEventArgs e)
{
// Drop the data item into corresponding treeview item
Point pos = e.GetPosition(mTreeView);
HitTestResult result = VisualTreeHelper.HitTest(mTreeView, pos);
if (result == null)
return; TreeViewItem selectedItem = Utils.FindVisualParent<TreeViewItem>(result.VisualHit);
if (selectedItem == null)
return; DataItem parent = selectedItem.Header as DataItem;
DataItem dataItem = e.Data.GetData(typeof(DataItem)) as DataItem;
if (parent != null && dataItem != null)
parent.Items.Add(dataItem);
}

  需要读者注意的则是该段代码中对GetData()函数的调用。Drag&Drop过程中,如果希望从DataObject中获取数据,那么必须使用原有类型。如A是B的基类,而DataObject中封存的则是类型B的实例,那么软件开发人员需要在DataObject.GetData()中使用typeof(B),而不能是typeof(A)。

  在实现拖拽功能的时候,软件开发人员需要注意一系列问题。

  首先是DragDropEffect。该枚举中的每个值都对应着拖放过程的一种特定行为。这些外观在UI设计和用户使用中拥有特定的惯用法。因此在开发过程中要想好到底希望对拖拽目标执行何种操作,以防止用户在使用过程中产生疑惑。

  另外,外部拖拽源也是一种常见的拖拽功能,如将文件拖拽到应用程序内以进行加载。在某些情况下,拖拽目标事件并不能提供所需的信息。如在拖拽多个文件到应用程序内的时候,IDataObject接口只提供了GetData()函数。在这种情况下,软件开发人员可以尝试将其转化为DataObject类型实例,并通过GetFileDropList()函数返回所有被拖拽的文件。

  同时需要注意的是鼠标位置的获取方法。在拖拽过程中,鼠标的位置不能通过Mouse类等WPF标准方法获得。在某些情况下,该方法将会返回一个错误的位置。这是因为在拖拽过程中,鼠标的控制权是由拖拽源所管理的。该管理过程中会使用Win32函数,从而使WPF无法正确地返回鼠标的位置信息。一个变通的方法则是使用PInvoke调用Win32 API GetCursorPos()。

  另一个需要提及的小技巧则是如何禁用拖拽。标准控件包括一些默认情况下可作为拖拽目标的控件,如TextBox。为了禁止该功能,软件开发人员可以将OnPreviewDragEnter()和OnPreviewDragOver()重载中DragEventArgs的Handled属性设置为true,并设置Effects为None,以模拟禁止拖放的效果。

源码下载:http://download.csdn.net/detail/silverfox715/3884722

转载请注明原文地址:http://www.cnblogs.com/loveis715/archive/2011/12/05/2277384.html

商业转载请事先与我联系:silverfox715@sina.com

WPF拖放功能实现zz的更多相关文章

  1. WPF拖放功能实现

    写在前面:本文为即兴而作,因此难免有疏漏和词不达意的地方.在这里,非常期望您提供评论,分享您的想法和建议. 这是一篇介绍如何在WPF中实现拖放功能的短文. 首先要读者清楚的一件事情是:拖放主要分为拖放 ...

  2. WPF开发快速入门【7】WPF的拖放功能(Drag and Drop)

    概述 本文描述WPF的拖放功能(Drag and Drop). 拖放功能涉及到两个功能,一个就是拖,一个是放.拖放可以发生在两个控件之间,也可以在一个控件自己内部拖放.假设界面上有两个控件,一个Tre ...

  3. 如何使用LightningChart拖放功能进行数据转移 ?

    本文主要介绍如何使用LightningChart扩展拖放功能为所有图表组件创建图表,如:系列,标题,轴线等等.支持用鼠标放置自定义对象到另一个图表中,如:可以添加或修改JSON/CSV或其他格式的数据 ...

  4. Draggabilly – 轻松实现拖放功能(Drag & Drop)

    Draggabilly 是一个很小的 JavaScript 库,专注于拖放功能.只需要简单的设置参数就可以在你的网站用添加拖放功能.兼容 IE8+ 浏览器,支持多点触摸.可以灵活绑定事件,支持 Req ...

  5. 脚本div实现拖放功能

    脚本div实现拖放功能 网页上有很多拖曳的操作,比如拖动树状列表,可拖曳的图片等. 1.原生拖放实现 <!doctype html> <html lang="en" ...

  6. 小强的HTML5移动开发之路(16)——神奇的拖放功能

    来自:http://blog.csdn.net/dawanganban/article/details/18181273 在智能手机发展飞速的现在拖放功能已经成为一种时尚,但是在我们的浏览器上是不是还 ...

  7. JavaScript如何实现拖放功能

    1.在学习ExtJs时,对其拖放功能感到很陌生,然后找了个拖放功能实现. 转载地址 2.拖拽的基本原理就是根据鼠标的移动来移动被拖拽的元素.鼠标的移动也就是x.y坐标的变化:元素的移动就是style. ...

  8. HOW TO: 在 Visual C# .NET 应用程序中提供文件拖放功能

    本文假定您熟悉下列主题: Windows 窗体列表框控件 Windows 窗体事件处理 生成示例的步骤 列表框控件提供了您需要处理的两个拖放事件: DragEnter 和 DragDrop. 当您在控 ...

  9. 一步一步学Silverlight 2系列(5):实现简单的拖放功能

    述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...

随机推荐

  1. XMPP框架下微信项目总结(2)授权登陆/注销/注册/打印日志

    xmpp授权登陆步骤1 初始化xmppstream 连接服务器 传递属性jid(IP地址 端口号)2 连接成功后 传递“登”陆密码授权 3 授权后,发送在线消息xmpp所有的代理都是子线程中调用的,处 ...

  2. hdu 5108 Alexandra and Prime Numbers

    数论题,本质是求出n的最大质因子 #include<time.h> #include <cstdio> #include <iostream> #include&l ...

  3. IT人学习方法论(一):学习方向

    07年的时候曾经讲过一节Webcast,名叫<使您成为Windows专家的一些学习习惯 >.直到最近,还经常收到听众关于这一节课反馈和心得的电子邮件,可见学习方法论是大家非常关心的问题.因 ...

  4. C#的面向对象特性之多态

    using System; using System.Collections; using System.Collections.Generic; namespace codeTest { class ...

  5. ***PHP Notice: Undefined index: ..问题的解决方法

    首先,这个不是错误,是warning.所以如果服务器不能改,每个变量使用前应当先定义. 方法1:服务器配置修改     修改php.ini配置文件,error_reporting = E_ALL &a ...

  6. 几年前做家教写的C教程(之二)

    C语言学习宝典(2) 认识C语言中的运算符: (1)算术运算符   (+  -  *  /  %) (2)关系运算符    (>  <  ==  >=  <=  != ) (3 ...

  7. HDU1294 Rooted Trees Problem(整数划分 组合数学 DP)

    讲解见http://www.cnblogs.com/IMGavin/p/5621370.html, 4 可重组合 dfs枚举子树的节点个数,相乘再累加  1 #include<iostream& ...

  8. iOS和Android的app界面设计规范(转)

    记录一下iOS和Andoird的界面设计规范,方便进行标准的产品设计,并与设计师顺畅沟通 iOS篇 界面尺寸 设备 分辨率 状态栏高度 导航栏高度 标签栏高度 iPhone6 plus 1242×22 ...

  9. PopupWindow响应返回键的问题

    假设情景是这样的:在一个Activity中弹出一个PopupWindow,要求在按返回键时关闭该PopupWindow. 如果该PopupWindow是无焦点的(默认情况),那么可以在Activity ...

  10. html table之 全选,全不选

    就是这个小功能让我和组长引发争端,就是这个小功能让我差点"被"辞职,就是这个自封装的js方法让我放下了对组长的敬畏之心,现在分享一下,其实也很简单,但是真的有这么简单吗? < ...