在CListCtrl之间及CListCtrl内部实现拖放
源码下载
参考我的资源里.上传后地址还没出来,等出来后再放上
介绍
本篇文章将介绍如何实现使用拖放方法从一个CListCtrl移动项目到另一个CListCtrl,同样也显示了如何使用拖放
从在CListCtrl内部实现改变项目顺序。这是我很早就想学的一些东西,并且同样在Code Project看到很多关于这个的问题。
特别是如何在一个CListCtrl内部重排项目顺序。我希望这篇文章能回答那些问题。我很肯定还有其他方法可实现同样功能,
也许那些方法更好用,但我这个对我来说已经工作的很好了。
首先,我要先说明,网上的微软知识库有一个很好的工程例子(http://support.microsoft.com/default.aspx?scid=kb;en-us;Q148738),
它给我很大帮助。我的代码跟那个很像。那个例子说明了从CListView拖放到CTreeView。那跟我现在要介绍的有一些细微差别。不过微软
知识库的例子只给了代码,没有说明文字,尽管它有很好的注释。
同样,这篇文章假设你已经至少很熟悉如何使用CListCtrl了。在Code Project有两篇很好的文章介绍了如何使用CListCtrl和CHeadCtrl控件。
并且Chris Maunder写了一篇非常好的文章介绍使用CListCtrl的回调。如果你没有阅读过他们,请赶快阅读下哦。
概要:
Drag 和 Drop操作至少涉及以下操作步骤:
1.你处理拖放操作的通知消息(即用户点了鼠标左键,并开始拖动)
2.你必须跟踪鼠标的移动,那时用户在拖动项目。
3.最后,当鼠标按键释放时,实现实际的复制和移动动作。
对话框需要添加的成员变量:
在我们完成拖放操作时需要保存一些信息,所以我们给对话框添加一些成员变量,他们只需要实现为protected成员,因为只需要内部使用。
m_nDragIndex 是我们将要移动的CListCtrl项目索引
m_nDropIndex 是CListCtrl中鼠标将要停放的索引
m_pDragWnd 和 m_pDropWnd 是拖放涉及的CListCtrl窗口指针
m_bDragging 标识我们正在拖放操作. 在MouseMove函数中使用以便使我们知道何时跟踪
m_pDragImage 是一个CImageList对象指针. MFC使得创建和管理拖放对象的图片变得非常容易
最后需要一个数据结构保存从一个列表拖到另一个列表 (或在同一个列表当中从一个项目到另一个项目)的信息.该结构如下:
这个结构包含一个LVITEM指针和一个字符串。我们使用LVITEM指针来实现CListCtrl的GetItem,InsertItem,及其他操作。
CString保存CListCtrl的第二列的数据。如过你有超过两列数据,只要添加更多的字符串。
捕获Drag操作的通知信息:
使用向导添加处理LVN_BEGINDRAG消息的函数。顺便说下,如果你希望可以拖动多个控件,你需要在此消息为所有列表作处理。
首先,我们希望保存我们将拖动的项目索引。所以把那值保存到一个成员变量。我们在稍后使用这个索引来获取相关信息。
m_nDragIndex = pNMListView->iItem;
然后我们创建一个我们要拖动的项目图片。在这里MFC帮了我们很多。
有两个方法来实现这部分,最简单的是让MFC来帮你处理繁琐的操作,正如第一个方法所示。第二个方法也只是稍微复杂。但他
要求你创建使用的图片资源。第二个代码代码片断显示了该方法。
第一个方法:
第二个方法:
最后,我们调用SetCapture。这样我们保证了我们的控件(CListCtrl)能够接收所有的鼠标消息。
即便用户把项目拖离控件,甚至对话框,我们仍能接收到通知消息。直到我们释放了Capture(我们在松开右键时)。
或者直到另一个窗口Caoture了鼠标。
跟踪拖动过程:
使用类向导(ClassWizard),添加一个处理WM_MOUSEMOVE消息的函数。
只有在m_bDragging标记设置为TRUE时,才开始处理逻辑。
下一节我们就实际处理在屏幕移动拖动图片
DragShowNolock(false)调用允许窗口平滑显示拖动图片。如果我们不调用这个,拖动图片有时会显示它经过的画面。
WindowFromPoint(pt)返回pt所在点的窗口。让我们知道鼠标指向哪里,他是否指向了CListCtrl窗口。
这节仅仅处理高亮显示我们拖动经过的CListCtrl项目
当我们移动鼠标的时候,我们需要保持跟踪我们拖动过什么窗口,这样当我们释放鼠标时我们知道把项目放到哪个控件上了。
所以我们把它保留到我们的成员变量。
IsKindOf(RUNTIME_CLASS(CListCtrl))是另一个常用函数,允许我们判断鼠标移动过的窗口是否是CListCtrl。这节处理鼠标移动过
的项目高亮显示。
完成拖放,处理放的动作:
我们在此几乎完成了所有动作。再次使用类向导,添加一个WM_LBUTTONUP消息的处理函数。这部分很短很精致。
和上面的OnMouseMove函数一样,我们仅仅处理和拖放相关的代码
不要忘记释放捕捉的鼠标。
当我们释放了物件,我们不再处理拖动处理。
MFC再次给我们提供了函数组来处理拖动图片。不要忘记删除上边创建的(拖动开始时创建)那个图片。
跟我们在OnMouseMove函数中处理的那样,我们找出鼠标在哪,以及所处的窗口。然后我们检测是否是一个CListCtrl窗口。
如果是,我们处理实际的放动作。为了容易阅读。我让处理复制和移动的代码打包进DropItemOnList函数中(如下所示)
然后进入最后的步骤,实际的复制/移动代码的处理。
实际复制/移动代码:
你需要手工添加一个函数来处理实际的复制/移动代码。这个函数可以是protected成员,
以下是实际的函数:
我们简单的设置LVITEM结构同时用来获取拖放来源控件的信息,以及添加该信息进拖放目标控件。
如果你的CListCtrl控件包含图标,不要忘记改变"lvi.mask=LVIF_TEXT"所在行。
所以我们设置来LVITEM结构,然后使用GetItem(&lvi)来获取我们拖动的列表项目的信息。
但你将注意到这个不会获取第二列的信息。那时我们为什么需要lvItem结构,我们使用pItem->sCol2=pDragList->GetItemText(pItem->plvi->iItem, 1);
来获取那个信息。
当我们处理拖动多项选择时,我们在lvItem对象设置一个CList,我们处理拖动来源CListCtrl并获取所有选项信息。然后如果我们处理的是Move操作,
我们删除所有来源的选项,然后通过处理CList并添加每一个项目到CListCtrl。
正如我的样例程序所实现,我检测用户是否拖动项目到其他的CListCtrl还是在同一个CListCtrl。如果用户释放一个项目到其他的CListCtrl,然后我们会
复制项目到其它控件,如果,用户仅仅拖放到同一个控件的不同点,我们将移动项目到新的索引(这是如何实现用户给一个列表按拖动来排序)。一般来说,
如果用户移动同一个控件的项目,我们会在添加回原列表前删除原来项目(获取完项目的信息后)。我们必须先删除,因为如果我们先添加的话,索引将不一致。
并且在很多实例中我们不允许将信息写到已存在的项目中。试着思考我所说的意思。在我们添加信息回列表后删除该项目,拖动列表的项目并看看发生了什么。
(译者注:最后一句我也不知道说的啥,只能猜测,所以把原文抄上: Try it to see what I mean. Move the part where we delete the item to AFTER we have added the item to the list. Drag items around in the list and see what happens.)
最后,我们添加项目到CListCtrl并设置该项目为高亮。
很简单,不是吗?