C# 拖放操作源码详解2
拖放操作当然并非只限于文字,有许多应用程序都会提供拖放图片的功能,以便提升操作的便利性。事实上不管是拖放哪一种类型的数据,其间的方法都没有太大的差异。
程序范例CH8_DemoForm012.cs示范如何在两个PictureBox控件间拖曳图片,其功能特性如下所示:
- 如图8.13所示,您可以使用拖放方式将左侧PictureBox控件中的图片移动至右侧的PictureBox控件中,反之亦然;即左右两个PictureBox控件都可以作为拖放来源与置放目标。
- 值得一提的是,如果您持续按Ctrl键,则可以使用拖放方式将左侧PictureBox控件中的图片复制到右侧的PictureBox控件中(如图8.14所示),反之亦然;即左右两个PictureBox控件都可以作为拖放来源与置放目标。
图8.13通过拖放操作来移动图片
图8.14通过拖放操作来复制图片
程序范例CH8_DemoForm012.cs在拖放操作方面的程序代码如下所示:// 声明一个常量以便侦测在拖曳期间 Ctrl 键是否被按下。
private void CH4_DemoForm065_Load(object sender, EventArgs e)
{
// 由于目前无法在设计工具中去设定 PictureBox 控件
// 的 AllowDrop 属性,所以必须通过程序代码来加以设定。
picLeft.AllowDrop = true;
picRight.AllowDrop = true;
}
// 处理左右两个 PictureBox 控件的 MouseDown 事件。
// 当鼠标指针位于控件的范围内而且鼠标按键被按下时便会引发此事件。
private void PictureBox_MouseDown(System.Object sender,
System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
PictureBox pic = (PictureBox)(sender);
//初始化拖放操作。
if (pic.Image != null)
{
pic.DoDragDrop(pic.Image,
DragDropEffects.Move | DragDropEffects.Copy);
}
}
}
// 处理左右两个 PictureBox 控件的 DragEnter 事件。
// 当某一个对象被拖曳至控件的范围内时就会引发该控件的 DragEnter 事件。
private void PictureBox_DragEnter(System.Object sender,
System.Windows.Forms.DragEventArgs e)
{
// 检查被拖曳资料的类型是否适用于目标控件。如果不适用,则拒绝置放。
if (e.Data.GetDataPresent(DataFormats.Bitmap))
{
// 如果在拖曳期间按 Ctrl 键,则执行复制操作;反之,则执行移动操作。
if ((e.KeyState & CtrlMask) == CtrlMask)
{
e.Effect = DragDropEffects.Copy;
}
else
{
e.Effect = DragDropEffects.Move;
}
}
else
{
e.Effect = DragDropEffects.None;
}
}
// 处理左右两个 PictureBox 控件的 DragDrop 事件。其实只要转换发送者(sender)然后检查 Name 属性以便确认哪一个 PictureBox 控件要移除影像,就可以使用同一个事件处理函数来处理两个 PictureBox控件的 DragDrop 事件。
private void PictureBox_DragDrop(System.Object sender,
System.Windows.Forms.DragEventArgs e)
{
PictureBox pic = (PictureBox)(sender);
pic.Image = (Bitmap)(e.Data.GetData(DataFormats.Bitmap));
// 如果 Ctrl 键没有被按下的话,就使另外一个 PictureBox 控件(也就是在 DragDrop 事件中并不是 sender 的那一个 PictureBox 控件)中的图像被移除。
if ((e.KeyState & CtrlMask) != CtrlMask)
{
if (pic.Name == "picLeft")
{
picRight.Image = null;
}
else
{
picLeft.Image = null;
}
}
}
前面这两个关于文字与图片的拖放操作范例都是在同一个窗体上的两个控件间进行,其实它们也可在同一个应用程序的不同窗体上的控件间拖放。下一个程序范例将示范如何接受从另一个应用程序拖放而来的项目,在此程序范例中,将接受从Windows资源管理器拖放而来的文件。
拖放文件
在Windows资源管理器中使用拖放操作来移动或复制文件是大家所惯用的方式。Windows资源管理器充分支持拖放操作,而且这也是非常多用户所偏爱的文件使用方式。此外,许多用户非常习惯直接从Windows资源管理器将文件拖放至对应的应用程序中来打开它们。例如,从Windows资源管理器将一个.doc 文档拖放至Microsoft Word即会将该文档在Microsoft Word中打开。
图8.15示范如何从Windows资源管理器中拖放文件
图8.15所示是程序范例CH8_DemoForm013.cs的运行画面。显而易见地,您可以从Windows资源管理器将一个或多个文件拖放至窗体上的ListBox控件中,而被拖放的文件的文件名会被添加到ListBox控件中。以下是CH8_DemoForm013.cs的程序代码内容:
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.All;
}
}
private void ListBox1_DragDrop(object sender, DragEventArgs e)
{
if(e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] MyFiles;
int i;
// 将文件赋给一个数组。
MyFiles = (string[])(e.Data.GetData(DataFormats.FileDrop));
// 循环处理数组并将文件添加到列表中。
for(i = 0;i <= MyFiles.Length - 1;i++)
{
ListBox1.Items.Add(MyFiles[i]);
}
}
}
图8.16在两个列表间来回拖放一个或多个文件来移动项目
请注意我们在ListBox控件的DragEnter事件处理函数中将Effect属性设定成DragDropEffects.All。由于文件本身实际上并没有被移动或复制,因此拖放源如何设定AllowedEffects将无关紧要,设定All表示对任何的FileDrop都会启用置放。
就本范例而言,DataFormats.FileDrop格式会含有每一个被置放文件的完整路径。本范例的操作逻辑是将所有被拖放文件的完整路径添入ListBox控件中,当然,您可以采用其他方法,比方说,您可以将被拖放的文件在一个MDI(多文件界面)文件窗口中打开。
在两个列表之间来回拖放项目
另外一项常见的拖放需求是,在两个列表(ListView控件)之间来回拖放项目。事实上,我们经常会通过一组按钮来将列表中被选取的项目移至另外一个列表中,不过这样的操作模式需要两次鼠标按键操作(第一次选取项目,第二次单击按钮)。显然,在这样的操作需求中,拖放操作会较受青睐,因为它只需单一动作即可完成(选取并拖曳)。
图8.16所示是程序范例CH8_DemoForm014.cs的运行画面。显而易见地,您可以在两个列表间来回拖放一个或多个文件来移动项目。本程序范例的设计重点说明如下:
- 由于两个列表的ListView控件都可以作为置放目标,因此务必将这两个ListView控件的AllowDrop属性设定成True。
- 请将两个ListView控件的MultiSelect属性设定成True。
- 请将两个ListView控件的FullRowSelect属性设定成True。
- 以下是程序范例CH8_DemoForm014.cs的程序代码内容。于Load事件处理函数中所调用的 PopulateListView() 程序主要是用来初始化两个ListView控件:
{
this.PopulateListView();
}
private void ListView_ItemDrag(object sender,
System.Windows.Forms.ItemDragEventArgs e)
{
ListViewItem[] myItems =
new ListViewItem[((ListView)(sender)).SelectedItems.Count];
int i = 0;
// 循环处理拖放来源的 SelectedItems 集合。
foreach(ListViewItem myItem in
((ListView)(sender)).SelectedItems)
{
// 将ListViewItem新增至ListViewItems的数组中。
myItems[i] = myItem;
i = i + 1;
}
// 建立一个DataObject对象来包含ListViewItem的数组。
((ListView)(sender)).DoDragDrop(new
DataObject("System.Windows.Forms.ListViewItem()",
myItems), DragDropEffects.Move);
}
private void ListView_DragEnter(object sender,
System.Windows.Forms.DragEventArgs e)
{
// 检查自定义的 DataFormat ListViewItem 数组。
if (e.Data.GetDataPresent(
"System.Windows.Forms.ListViewItem()"))
{
e.Effect = DragDropEffects.Move;
}
else
{
e.Effect = DragDropEffects.None;
}
}
private void ListView_DragDrop(object sender,
System.Windows.Forms.DragEventArgs e)
{
ListViewItem[] myItems =
(ListViewItem[])(
e.Data.GetData("System.Windows.Forms.ListViewItem()"));
int i = 0;
foreach (ListViewItem myItem in myItems)
{
// 将项目添加到目标列表中。
ListViewItem item = new ListViewItem(myItems[i].Text);
item.SubItems.Add(myItems[i].SubItems[1].Text);
((ListView)(sender)).Items.Add(item);
// 从源列表移除项目。
if (sender == ListView1)
{
istView2.Items.Remove(ListView2.SelectedItems[0]);
}
else
{
ListView1.Items.Remove(ListView1.SelectedItems[0]);
}
i = i + 1;
}
}
您或许会觉得奇怪,为什么不使用ListBox控件而要使用ListView控件。最主要的理由是,ListBox控件并不支持拖曳多个项目,单击列表会使得多重选取失效。
ListView与TreeView控件都拥有一个ItemDrag事件来促进拖曳操作。在本范例中,我们使用单一个ItemDrag事件处理函数来处理两个ListView控件的ItemDrag事件。其中的sender参数代表初始化拖曳的控件。
由于DataFormats类的成员并不包括ListViewItem类型,所以数据必须当作一个系统Type来传递。ItemDrag事件处理函数的程序代码会创建一个类型为ListViewItem的数组并循环处理SelectedItems集合以便添入数组。在DoDragDrop方法中,一个新的DataObject对象会被创建并以数组来添入。您可以使用相同的技巧来拖放任何的系统Type。
在DragDrop事件处理函数中,我们会将DataObject对象中的数组复制到一个新的ListViewItem数组中,而且每一个ListViewItem会被添加到目标ListView控件的Items集合中。
在两个TreeView之间来回拖放节点
程序范例
图8.17与图8.18所示是程序范例CH8_DemoForm015.cs的运行画面。显而易见地,您可以在两个TreeView控件间来回拖放一个节点(移动或复制)。本程序范例的程序代码如下所示:// 声明一个常量以便侦测在拖曳期间 Ctrl 键是否被按下。
// 处理两个 TreeView 控件的 ItemDrag 事件。
private void TreeView_ItemDrag(System.Object sender,
System.Windows.Forms.ItemDragEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// 初始化拖放操作。
DoDragDrop(e.Item,
DragDropEffects.Move | DragDropEffects.Copy);
}
}
// 处理两个 TreeView 控件的 DragDrop 事件。
private void TreeView_DragDrop(System.Object sender,
System.Windows.Forms.DragEventArgs e)
{
// 此变量用来持有被用户所拖曳的节点。
TreeNode OriginationNode =
(TreeNode)(
e.Data.GetData("System.Windows.Forms.TreeNode"));
// 为一个TreeView控件调用GetDataPresent方法与为一个文字或图像的方式一点不同,原因是TreeNode并不是DataFormats类的一个成员。也就是说,它不是一个预先定义的类型。诸如此种状况,您必须使用能够接受一个字符串作为类型的重载版本。
if (e.Data.GetDataPresent(
"System.Windows.Forms.TreeNode", false))
{
Point pt;
TreeNode DestinationNode;
// 取得鼠标指针所在位置的工作区坐标(Client Coordinate)。
pt = ((TreeView)(sender)).PointToClient(
new Point(e.X, e.Y));
// 选取鼠标指针所在位置之下的节点。
DestinationNode = ((TreeView)(sender)).GetNodeAt(pt);
// 此处的 If 语句用来确保当用户在他们尝试去拖曳节点的上方不小心放开鼠标按键的话,不会失去节点。如果您没有去检查目标节点是否就是源节点,将会使得该节点消失。
if (DestinationNode.TreeView != OriginationNode.TreeView)
{
DestinationNode.Nodes.Add(
(TreeNode)(OriginationNode.Clone()));
// 当添加一个新的节点时展开父节点,如此才会清楚地呈现出拖放操作的结果。如果没有这样做的话,将会显示一个 + 号。
DestinationNode.Expand();
// 如果 Ctrl 键没有被按下,就将原来的节点移除
// 以便实现移动节点的拖放操作。
if ((e.KeyState & CtrlMask) != CtrlMask)
{
OriginationNode.Remove();
}
}
}
}
// 处理两个 TreeView 控件的 DragEnter 事件。
private void TreeView_DragEnter(System.Object sender,
System.Windows.Forms.DragEventArgs e)
{
// 检查被拖曳的数据的类型是否适用于目标控件。如果不适用,则拒绝置放。
if (e.Data.GetDataPresent(
"System.Windows.Forms.TreeNode"))
{
// 如果在拖曳期间按着 Ctrl 键,则执行复制操作;反之,则执行移动操作。
if ((e.KeyState & CtrlMask) == CtrlMask)
{
e.Effect = DragDropEffects.Copy;
}
else
{
e.Effect = DragDropEffects.Move;
}
}
else
{
e.Effect = DragDropEffects.None;
}
}
图8.17使用拖放操作来移动节点
图8.18使用拖放操作来复制节点
转载于:https://www.cnblogs.com/jxsoft/archive/2011/05/25/2056488.html
C# 拖放操作源码详解2相关推荐
- udhcp源码详解(五) 之DHCP包--options字段
中间有很长一段时间没有更新udhcp源码详解的博客,主要是源码里的函数太多,不知道要不要一个一个讲下去,要知道讲DHCP的实现理论的话一篇博文也就可以大致的讲完,但实现的源码却要关心很多的问题,比如说 ...
- Go 语言 bytes.Buffer 源码详解之1
转载地址:Go 语言 bytes.Buffer 源码详解之1 - lifelmy的博客 前言 前面一篇文章 Go语言 strings.Reader 源码详解,我们对 strings 包中的 Reade ...
- Go bufio.Reader 结构+源码详解
转载地址:Go bufio.Reader 结构+源码详解 I - lifelmy的博客 前言 前面的两篇文章 Go 语言 bytes.Buffer 源码详解之1.Go 语言 bytes.Buffer ...
- 封装成jar包_通用源码阅读指导mybatis源码详解:io包
io包 io包即输入/输出包,负责完成 MyBatis中与输入/输出相关的操作. 说到输入/输出,首先想到的就是对磁盘文件的读写.在 MyBatis的工作中,与磁盘文件的交互主要是对 xml配置文件的 ...
- Redis从精通到入门——数据类型Zset实现源码详解
Redis数据类型之Zset详解 Zset简介 Zset常用操作 应用场景 Zset实现 源码阅读 Zset-ziplist实现 图解Zset-ziplist Zset-字典(dict) + 跳表(z ...
- java的数组与Arrays类源码详解
java的数组与Arrays类源码详解 java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用. 类的 ...
- Linux 内核中RAID5源码详解之守护进程raid5d
Linux 内核中RAID5源码详解之守护进程raid5d 对于一个人,大脑支配着他的一举一动:对于一支部队,指挥中心控制着它的所有活动:同样,对于内核中的RAID5,也需要一个像大脑一样的东西来支配 ...
- 李沐d2l《动手学深度学习》第二版——风格迁移源码详解
本文是对李沐Dive to DL<动手学深度学习>第二版13.12节风格迁移的源码详解,整体由Jupyter+VSCode完成,几乎所有重要代码均给出了注释,一看就懂.需要的同学可以在文末 ...
- AidLux“换脸”案例源码详解 (Python)
"换脸"案例源码详解 (Python) faceswap_gui.py用于换脸,可与facemovie_gui.py身体互换源码(上一篇文章)对照观看 打开faceswap_gui ...
最新文章
- 阿里云OSS上传图片实现流程
- 零基础,最完整的WordPress建站教程
- C语言初始化错误怎么办,结构体变量的初始化错误
- boost::graph模块实现从连接的平面图开始并添加边以使图最大平面化
- springboot通过url访问项目外的其他目录下的图片
- ef core mysql 生成迁移失败_EFCore + MySql codeFirst 迁移 Migration出现的问题
- 切记!构造函数里面别一定不要初始化其他类,踩过坑的都知道
- VC++动态链接库编程(转载)
- 12月19日绝地求生服务器维护公告,绝地求生12月19日更新到几点 绝地求生正式服更新维护公告...
- 恒大继续押宝造车 9.3亿美元收购NEVS的51%股权
- 关于地理数据坐标转换
- Java如何与Simon Ritter-JDK 9到13一起发展
- 张飞硬件90天学习笔记——第01天个人记录,完整请看我的个人简介/主页
- 智能分析的所见即所得——基于Lambda架构的实时数据引擎
- PSO算法求解全局最大值
- 冷却水的循环方式有哪几种_循环水处理设备冷却系统分为哪些
- 目标检测训练数据扩充增强工具使用说明
- Bootstrap 中的 aria-label 和 aria-labelledby
- 使用swift语言和xmpp framework开发聊天软件
- 知识蒸馏论文阅读:FGD算法笔记
热门文章
- Delphi使用资源文件全攻略
- 半阈值化的应用说明及利用函数threshold实现半阈值化的方法
- java lock 效率_工作常用4种Java线程锁的特点,性能比较、使用场景
- leetcode算法题--最低票价★
- python给图片添加字符
- mysql 转义字符 escape_mysql_real_escape_string转义字符串返回false?
- 软件详细设计说明书_互联网知识大全:软件开发中和各种开发软件文档的常见的英文缩写,还不快快收藏!...
- 无效字符 java_Java知识查漏补缺
- Go goroutine理解
- 黑暗城堡 最短路径生成树