CSharp Tips:Drag & Drop的高级应用
2010-11-18 15:38
316 查看
昨天,有同事问了我一个问题,这个问题的第一种描述方式是“怎么利用拖放的方式把一个进程中的流传递到第二个进程中”;第二种描述方式是“把应用程序中的数据(文件/流)通过拖放的方式复制到桌面或者Explorer中”。看似简单的描述下面隐藏了不少东西,有点意思,于是昨晚翻了翻资料。
这个问题涉及到两个技术点:跨进程的大数据块的传递,Drag & Drop。
跨进程的大数据块传递不考虑管道/Socket这类直接通讯方式的话,间接方式也很多,DDE、剪贴板、文件或者内存镜像文件。DDE有点古老,我都不记得该怎么玩了。实时性要求不高的话,保存成为临时文件,将文件名传递过去,是一个无论从系统开销,还是实现上都是一个不错的选择。至于内存镜像文件,是另一个专题,不在这里赘述了。剪贴板也是个比较有趣的东西,真要说的话,也有很多话可讲,也不在这里多说了。
数据传递的方案定了,再看Drag & Drop。在MSDN中查了一下,System.Window.Form.Control下有一组和拖放相关的方法和事件,例如DoDragDrop用来作为源启动一次拖放,DragEnter、DragOver、DragDrop是作为目标处理拖放的事件。整个拖放过程就是源在拖放开始的时候提交需要拖放的数据,并指明影响方式(例如Copy、Move之类),目标在拖放被触发(Drop的时候)时获取拖放数据并且进行相应处理。数据和数据格式是通过一个叫IDataObject的接口来描述的,它用来存储数据,申明支持的数据格式,并且对数据格式进行转换。思路清楚了,关键要实现一个IDataObject。
回过头看我们的问题,先解决“把应用程序中的数据(文件/流)通过拖放的方式复制到桌面或者Explorer中”这种情况。写一个小例子,一个窗口上面只有一个ListBox,里面有两个元素,分别对应硬盘上的两个文件,希望在ListBox中选中一个元素拖放到桌面,就把那个元素对应的文件复制到桌面。
代码逻辑很简单,处理ListBox的鼠标事件,判断是不是满足启动拖放的条件,当启动拖放时,把当前选中的元素封装到我们定义的IDataObject中,然后调用DoDragDrop方法。问题是IDataObject如果定义?有同学就问了我怎么知道桌面(Explorer)要求的数据格式是什么?易同学就这么问过。这也很简单,只要看看桌面作为拖放源的时候生成的DataObject支持那些格式,数据是什么形式的,仿照一下就行了。
在开始正式工作之前,先在窗口的DragEnter中写一段代码,检查DataSource支持的格式,运行程序,在桌面上拖到一个我们到我们写的应用程序窗口,一看输出,居然支持8种格式。作为工程师一定要把复杂的问题变得简单,看名字一分析,有几个格式叫“FileDrop”、“FileName”、“FileNameW”,返回的数据类型是一个字符串数组,每个成员是一个文件名,不用想,就和这些格式有关。马上动手,IDataObject类的实现如下,完整代码可到此处下载。
运行程序,拖动ListBox中的元素到桌面,成了,文件复制过去了。在进一步测试,其实只需要支持“FileDrop”的格式就可以了。
再看另一种情况,从自己写的一个应用程序拖放到另一个自己写的应用程序,怎么办?更好办了,双方约定一下IDataObject实现类需要支持的格式,目标应用程序在自己的DragDrop时间中获取数据再执行操作就可以了。
最后提一点DoDragDrop方法的第一个参数,是支持字符串类型的,MSDN提供的示例也是传递一个字符串,但是跨进程的话应该不能直接用,就算是自己的程序估计也不行,跨进程一定需要自己定义一个实现IDataObject的类,想想COM,总要做Marshal的。这段个人猜测,没有验证,供参考。
参考文档
Ø Control.DoDragDrop方法
这个问题涉及到两个技术点:跨进程的大数据块的传递,Drag & Drop。
跨进程的大数据块传递不考虑管道/Socket这类直接通讯方式的话,间接方式也很多,DDE、剪贴板、文件或者内存镜像文件。DDE有点古老,我都不记得该怎么玩了。实时性要求不高的话,保存成为临时文件,将文件名传递过去,是一个无论从系统开销,还是实现上都是一个不错的选择。至于内存镜像文件,是另一个专题,不在这里赘述了。剪贴板也是个比较有趣的东西,真要说的话,也有很多话可讲,也不在这里多说了。
数据传递的方案定了,再看Drag & Drop。在MSDN中查了一下,System.Window.Form.Control下有一组和拖放相关的方法和事件,例如DoDragDrop用来作为源启动一次拖放,DragEnter、DragOver、DragDrop是作为目标处理拖放的事件。整个拖放过程就是源在拖放开始的时候提交需要拖放的数据,并指明影响方式(例如Copy、Move之类),目标在拖放被触发(Drop的时候)时获取拖放数据并且进行相应处理。数据和数据格式是通过一个叫IDataObject的接口来描述的,它用来存储数据,申明支持的数据格式,并且对数据格式进行转换。思路清楚了,关键要实现一个IDataObject。
回过头看我们的问题,先解决“把应用程序中的数据(文件/流)通过拖放的方式复制到桌面或者Explorer中”这种情况。写一个小例子,一个窗口上面只有一个ListBox,里面有两个元素,分别对应硬盘上的两个文件,希望在ListBox中选中一个元素拖放到桌面,就把那个元素对应的文件复制到桌面。
代码逻辑很简单,处理ListBox的鼠标事件,判断是不是满足启动拖放的条件,当启动拖放时,把当前选中的元素封装到我们定义的IDataObject中,然后调用DoDragDrop方法。问题是IDataObject如果定义?有同学就问了我怎么知道桌面(Explorer)要求的数据格式是什么?易同学就这么问过。这也很简单,只要看看桌面作为拖放源的时候生成的DataObject支持那些格式,数据是什么形式的,仿照一下就行了。
在开始正式工作之前,先在窗口的DragEnter中写一段代码,检查DataSource支持的格式,运行程序,在桌面上拖到一个我们到我们写的应用程序窗口,一看输出,居然支持8种格式。作为工程师一定要把复杂的问题变得简单,看名字一分析,有几个格式叫“FileDrop”、“FileName”、“FileNameW”,返回的数据类型是一个字符串数组,每个成员是一个文件名,不用想,就和这些格式有关。马上动手,IDataObject类的实现如下,完整代码可到此处下载。
private class CDragingData : IDataObject { private static String[] g_asFormats = new String[] {"System.String","FileName","FileNameW","FileDrop" }; private String[] m_asData = null; public CDragingData(String data) { m_asData = new String[1]; m_asData[0] = data; } #region IDataObject Members public object GetData(Type format) { if (format != null && format.FullName == "System.String") return m_asData[0]; else return null; } public object GetData(string format) { if (String.IsNullOrEmpty(format)) return null; switch (format.ToUpper()) { case "STRING": case "SYSTEM.STRING": return m_asData[0]; case "FILENAME": case "FILENAMEW": case "FILEDROP": return m_asData; default: return null; } } public object GetData(string format, bool autoConvert) { return GetData(format); } public bool GetDataPresent(Type format) { if (format != null && format.FullName == "System.String") return true; else return false; } public bool GetDataPresent(string format) { if (String.IsNullOrEmpty(format)) return false; switch (format.ToUpper()) { case "FILENAME": case "FILENAMEW": case "FILEDROP": return true; default: return false; } } public bool GetDataPresent(string format, bool autoConvert) { return GetDataPresent(format); } public string[] GetFormats() { return g_asFormats; } public string[] GetFormats(bool autoConvert) { return g_asFormats; } public void SetData(object data) { if (data == null) m_asData[0] = null; else m_asData[0] = data.ToString(); } public void SetData(Type format, object data) { SetData(data); } public void SetData(string format, object data) { SetData(data); } public void SetData(string format, bool autoConvert, object data) { SetData(data); } #endregion }
运行程序,拖动ListBox中的元素到桌面,成了,文件复制过去了。在进一步测试,其实只需要支持“FileDrop”的格式就可以了。
再看另一种情况,从自己写的一个应用程序拖放到另一个自己写的应用程序,怎么办?更好办了,双方约定一下IDataObject实现类需要支持的格式,目标应用程序在自己的DragDrop时间中获取数据再执行操作就可以了。
最后提一点DoDragDrop方法的第一个参数,是支持字符串类型的,MSDN提供的示例也是传递一个字符串,但是跨进程的话应该不能直接用,就算是自己的程序估计也不行,跨进程一定需要自己定义一个实现IDataObject的类,想想COM,总要做Marshal的。这段个人猜测,没有验证,供参考。
参考文档
Ø Control.DoDragDrop方法
相关文章推荐
- [Tips]HTML5之禁止File Drag&Drop
- HTML5 drag & drop 拖拽与拖放简介
- Drag & Drop
- Drag & Drop - From List to VideoDisplay
- 第四部分:枚举FORMATETC(OLE drag&drop之旅)
- 多线程并发库高级应用 之 java5中的线程并发库--线程池、Callable&Future
- NLP︱高级词向量表达(一)——GloVe(理论、相关测评结果、R&python实现、相关应用)
- HTML5实现元素的原生拖放(drag&drop)【涉及Event对象】
- web拖动Drag&Drop原理
- MFC Drag & Drop
- Java中的Drag and Drop详解…
- 第五部分:Drop源(OLE drag&drop之旅)
- JavaScript: DHTML API,Drag & Drop for Images and Layers
- Redis 笔记与总结5 Redis 常用命令之 键值命令 和 服务器命令 && 高级应用之 安全性 和 主从复制
- 搭建属于你的家庭网络实时监控–HTML5在嵌入式系统中的应用·高级篇
- 四两拨千斤——Dijit Tree 拖拽(DnD, Drag & Drop)的精细控制
- 史上最强之dos命令-"FOR"-高级应用范例(來源於網絡http://blog.csdn.net/ahpo/archive/2006/06/19/812501.aspx)
- Android API之Drag&Drop
- Adding/removing fields and columns drag & drop bug's fix
- SVGElement Drag & Drop