ATLWTL第十部分.docx
- 文档编号:10909570
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:31
- 大小:569.68KB
ATLWTL第十部分.docx
《ATLWTL第十部分.docx》由会员分享,可在线阅读,更多相关《ATLWTL第十部分.docx(31页珍藏版)》请在冰豆网上搜索。
ATLWTL第十部分
第十部分-实现一个拖放源
∙下载示例工程-97KB
内容
∙简介
∙开始工程
∙文件打开处理
∙拖动源
∙拖动源的接口
∙用于调用者的辅助方法
∙IDropSource的方法
∙从查看器中拖放
∙加入MRU列表
∙设置MRU对象
∙处理MRU命令并更新列表
∙保存MRU列表
∙其他UIGoodies
∙透明的拖动图像
∙透明的选择矩形
∙标示排序的列
∙使用平铺视图模式
∙设置平铺视图的图像列表
∙使用平铺视图的图像列表
∙设置附加的文本行
∙版权和许可
∙修订历史
简介
拖放是许多流行应用的特性之一。
尽管实现一个放下目标相当简单,但拖动源却要复杂的多。
MFC中有两个类 COleDataObject 和COleDropSource 可以帮助管理拖动源所必须提供的数据,但WTL中没有这种辅助类。
对于我们这些WTL用户来说,幸运的是,RaymondChen 在2000年的时候在MSDN上写过一篇文章(“TheShellDrag/DropHelperObjectPart2”),其中有 IDataObject 的纯C++实现,这对于为WTL应用编制一个完整的拖放源提供了巨大的帮助。
本文的示例工程是一个CAB文件查看器,可以使你从CAB中提取文件,只要把它们从查看器里拖到资源浏览器窗口中即可。
本文还会讨论几个新的框架窗口话题,例如对文件打开的处理以及类似于MFC的文档/视图框架的数据管理。
我还会演示WTL的MRU(most-recently-used,最近使用)文件列表类,以及第六版的列表视图控件的几个新的UI特性。
重要提示:
你需要从微软下载并安装CABSDK来编译示例代码。
在KB文章 Q310618 中有此SDK的链接。
示例工程假定SDK位于和源代码相同的路径下的名为“cabsdk”的目录中。
记住,如果你在安装WTL或者编译示例代码时遇到了问题,请在这儿提问之前先阅读第一部分的readme一节。
开始工程
要开始我们的CAB查看器应用,需要运行WTLAppWizard并创建一个名为 WTLCabView 的工程。
它应该是一个SDI应用,所以在第一页中选择SDIApplication:
在下一页里,去掉 CommandBar,并把 ViewType 改为 ListView。
向导会为我们的视图窗口创建一个派生自 CListViewCtrl 的C++类。
视图窗口类看起来就是这样:
classCWTLCabViewView:
publicCWindowImpl
{
public:
DECLARE_WND_SUPERCLASS(NULL,CListViewCtrl:
:
GetWndClassName())
//Construction
CWTLCabViewView();
//Maps
BEGIN_MSG_MAP(CWTLCabViewView)
END_MSG_MAP()
//...
};
就像我们在第二部分里使用视图类一样,我们可以使用 CWindowImpl 的第三个模板参数设置缺省的窗口风格:
#defineVIEW_STYLES\
(LVS_REPORT|LVS_SHOWSELALWAYS|\
LVS_SHAREIMAGELISTS|LVS_AUTOARRANGE)
#defineVIEW_EX_STYLES(WS_EX_CLIENTEDGE)
classCWTLCabViewView:
publicCWindowImpl CWinTraitsOR { //... }; 由于在WTL中没有文档/视图框架,视图类需要做双份的工作,既是UI,也是存放有关CAB信息的地方。 在拖放操作中传递的数据结构为CDraggedFileInfo: structCDraggedFileInfo { //Datasetatthebeginningofadrag/drop: CStringsFilename;//nameofthefileasstoredintheCAB CStringsTempFilePath;//pathtothefileweextractfromtheCAB intnListIdx;//indexofthisiteminthelistctrl //Datasetwhileextractingfiles: boolbPartialFile;//trueifthisfileiscontinuedinanothercab CStringsCabName;//nameoftheCABfile boolbCabMissing;//trueifthefileispartiallyinthiscaband //theCABit'scontinuedinisn'tfound,meaning //thefilecan'tbeextracted CDraggedFileInfo(constCString&s,intn): sFilename(s),nListIdx(n),bPartialFile(false), bCabMissing(false) {} }; 视图类中还有如下方法: 初始化、管理文件列表,以及在拖放操作开始的时候准备一个 CDraggedFileInfo 列表。 由于本文是讲关于拖放的,所以我不会深入到UI工作的内部去,需要了解所有细节的话可以检视示例工程中的 WTLCabViewView.h。 文件打开处理 要查看一个CAB文件,用户可以使用 File-Open 命令并选择一个CAB文件。 向导为 CMainFrame 生成的代码包含了 File-Open 菜单项的一个处理器: BEGIN_MSG_MAP(CMainFrame) COMMAND_ID_HANDLER_EX(ID_FILE_OPEN,OnFileOpen) END_MSG_MAP() OnFileOpen() 使用了 CMyFileDialog 类,该类是在第九部分里介绍到的WTL的 CFileDialog 的增强版本,用以显示一个标准的文件打开对话框。 voidCMainFrame: : OnFileOpen( UINTuCode,intnID,HWNDhwndCtrl) { CMyFileDialogdlg(true,_T("cab"),0U, OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, IDS_OPENFILE_FILTER,*this); if(IDOK==dlg.DoModal(*this)) ViewCab(dlg.m_szFileName); } OnFileOpen() 调用了辅助函数 ViewCab(): voidCMainFrame: : ViewCab(LPCTSTRszCabFilename) { if(EnumCabContents(szCabFilename)) m_sCurrentCabFilePath=szCabFilename; } EnumCabContents() 相当的复杂,它使用CABSDK调用来枚举在 OnFileOpen() 中选中的文件的内容,并填充视图窗口。 不过 ViewCab() 现在还不完善,我们后面会给它加入支持MRU列表的代码。 下面是查看器的样子,其中显示着Windows98的某个CAB文件的内容: EnumCabContents() 使用了视图类中的两个方法来填充UI: AddFile() 和 AddPartialFile()。 AddPartialFile() 在当一个文件被部分存储在CAB中时被调用,因为其头部是在前面的CAB里。 在上面的截图里,列表中的第一个文件就是一个部分文件。 其余的文件是通过 AddFile()添加的。 这两个方法都会为要添加的文件分配一个数据结构,从而视图可以知道其显示的每个文件的所有相关细节。 如果 EnumCabContents() 返回真,则代表所有的枚举以及UI设置工作成功完成。 如果我们只是写一个简单的CAB查看器,我们就可以收手了,尽管此应用不那么有趣。 为了使它真正地有用,我们将对它添加拖放支持,以使用户可以从CAB中提取文件。 拖动源 拖放源是一个COM对象,它实现了两个接口: IDataObject 和 IDropSource。 IDataObject 用来存放在拖放操作中客户端需要传递的任意数据,在我们这种情况下,此数据应该是一个 HDROP 结构,其中列出了要从CAB中提取的文件。 IDropSource 的方法会由OLE调用,用以在拖放操作中向源通知事件。 拖动源接口 实现了我们的拖放源的C++类为 CDragDropSource。 该类以我在简介中提到过的 MSDN文章中的 IDataObject 实现为起始。 在该文中你可以找到所有代码相关的细节,因此我在这儿就不重复了。 然后我们再向类中加入 IDropSource 及其两个方法: classCDragDropSource: publicCComObjectRootEx publicCComCoClass publicIDataObject, publicIDropSource { public: //Construction CDragDropSource(); //Maps BEGIN_COM_MAP(CDragDropSource) COM_INTERFACE_ENTRY(IDataObject) COM_INTERFACE_ENTRY(IDropSource) END_COM_MAP() //IDataObjectmethodsnotshown... //IDropSource STDMETHODIMPQueryContinueDrag( BOOLfEscapePressed,DWORDgrfKeyState); STDMETHODIMPGiveFeedback(DWORDdwEffect); }; 用于调用者的辅助方法 CDragDropSource 使用几个辅助方法封装了 IDataObject 的管理以及拖放的通信。 一个拖放操作遵循以下模式: 1.主框架得到用户开始拖放操作的通知。 2.主框架调用视图窗口来创建一个被拖动的文件的列表。 视图在一个 vector 3.主框架创建一个 CDragDropSource 对象并将上述向量传递给它,以使它得知要从CAB中提取哪些文件。 4.主框架开始拖放操作。 5.如果用户在一个适当的拖放目标上放下,则 CDragDropSource 对象提取文件。 6.主框架更新UI以标示不能被提取的文件。 步骤3到6由辅助方法处理。 初始化在 Init() 方法中完成: boolInit(LPCTSTRszCabFilePath,vector Init() 将数据复制到保护成员中,填入到一个 HDROP 结构,并使用 IDataObject 方法将该结构保存到数据对象中。 Init() 还作了另一个重要的步骤: 它在TEMP目录下为每个拖动的文件创建了一个零字节的文件。 例如,如果用户从CAB文件中拖动 buffy.txt 和willow.txt,Init() 将在TEMP目录下使用这两个名字创建两个文件。 这是为了预防,万一拖放目标要验证从 HDROP 读入的文件名,如果文件不存在,则目标有可能会拒绝放下。 接下来的方法是 DoDragDrop(): HRESULTDoDragDrop(DWORDdwOKEffects,DWORD*pdwEffect); DoDragDrop() 接受 dwOKEffects 中的一组 DROPEFFECT_* 标志,这些标志表明了源所允许的那些动作。 它会查询必要的接口,然后调用DoDragDrop() API。 如果拖放成功,*pdwEffect 就被设置为用户希望执行的 DROPEFFECT_* 值。 最后一个方法是 GetDragResults(): constvector CDragDropSource 对象维护的 vector 如果某个文件被发现还连着另一个CAB,或者是不能被提取,则 CDraggedFileInfo 结构会被执行必要的更新。 主框架调用 GetDragResults() 来获取此向量,查找错误并相应更新UI。 IDropSource的方法 第一个 IDropSource 方法是 GiveFeedback(),它通知源,用户是想采取哪种操作(移动、复制或者链接)。 如果愿意的话源可以改变光标。 CDragDropSource 对操作保持了跟踪,并告诉OLE要使用缺省的拖放光标。 STDMETHODIMPCDragDropSource: : GiveFeedback(DWORDdwEffect) { m_dwLastEffect=dwEffect; returnDRAGDROP_S_USEDEFAULTCURSORS; } 另一个 IDropSource 方法是 QueryContinueDrag()。 OLE在用户把光标移来移去时调用此方法,并告诉源哪个鼠标键,以及键盘键,被按下了。 下边是大多数 QueryContinueDrag() 的实现所采用的样板代码: STDMETHODIMPCDragDropSource: : QueryContinueDrag( BOOLfEscapePressed,DWORDgrfKeyState) { //IfESCwaspressed,cancelthedrag. //Iftheleftbuttonwasreleased,dodropprocessing. if(fEscapePressed) returnDRAGDROP_S_CANCEL; elseif(! (grfKeyState&MK_LBUTTON)) { //IfthelastDROPEFFECTwegotinGiveFeedback() //wasDROPEFFECT_NONE,weabortbecausetheallowable //effectsofthesourceandtargetdon'tmatchup. if(DROPEFFECT_NONE==m_dwLastEffect) returnDRAGDROP_S_CANCEL; //TODO: ExtractfilesfromtheCABhere... returnDRAGDROP_S_DROP; } else returnS_OK; } 当我们发现左键被释放了,就到了我们要从CAB中提取选中的文件的地方了。 STDMETHODIMPCDragDropSource: : QueryContinueDrag( BOOLfEscapePressed,DWORDgrfKeyState) { //IfESCwaspressed,cancelthedrag. //Iftheleftbuttonwasreleased,dothedrop. if(fEscapePressed) returnDRAGDROP_S_CANCEL; elseif(! (grfKeyState&MK_LBUTTON)) { //IfthelastDROPEFFECTwegotinGiveFeedback() //wasDROPEFFECT_NONE,weabortbecausetheallowable //effectsofthesourceandtargetdon'tmatchup. if(DROPEFFECT_NONE==m_dwLastEffect) returnDRAGDROP_S_CANCEL; //Ifthedropwasaccepted,dotheextractinghere, //sothatwhenwereturn,thefilesareinthetempdir //andreadyforExplorertocopy. if(ExtractFilesFromCab()) returnDRAGDROP_S_DROP; else returnE_UNEXPECTED; } else returnS_OK; } CDragDropSource: : ExtractFilesFromCab() 是另一个复杂点的代码,它使用CABSDK把文件提取到TEMP目录下,覆盖掉我们先前创建的零字节的文件。 当 QueryContinueDrag() 返回 DRAGDROP_S_DROP 时,也即告诉了OLE完成此拖放操作。 如果拖放目标是一个资源浏览器窗口,资源浏览器会把文件从TEMP目录复制到发生拖放的目录。 从查看器中拖放 我们已经看过了实现拖放操作逻辑的类,现在,我们来看一下我们的查看器应用是怎样使用这个类的。 当主框架窗口接收到 LVN_BEGINDRAG 通知消息时,它会调用视图以获取选中文件的列表,而后设置 CDragDropSource 对象: LRESULTCMainFrame: : OnListBeginDrag(NMHDR*phdr) { vector CComObjectStack DWORDdwEffect=0; HRESULThr; //Getalistofthefilesbeingdragged(minusfiles //thatwecan'textractfromthecurrentCAB). if(! m_view.GetDraggedFileInfo(vec)) return0;//donothing //Initthedrag/dropdataobject. if(! dropsrc.Init(m_sCurrentCabFilePath,vec)) return0;//donothing //Startthedrag/drop! hr=dropsrc.DoDragDrop(DROPEFFECT_COPY,&dwEffect); return0; } 第一个调用的是视图的 GetDraggedFileInfo() 方法,用以得到选中文件的列表。 此方法返回一个 vector GetDraggedFileInfo() 在选定的文件都不能被提取的情况下(例如文件被分块存放在不同的CAB文件中)有可能失败。 如果发生了这种情况,则 OnListBeginDrag() 也静静地失败,不做任何事情就返回。 最后,我们调用 DoDragDrop() 来开始操作,并让 CDragDropSource 处理剩余的事情。 上面列出的步骤6提到了拖放结束后对UI的更新。 因为有可能在CAB末尾的一个文件仅仅是部分存储于此CAB中,而剩余的则在后续的一个CAB里。 (这在Windows9x的安装文件里非常普遍,在那儿CAB需要能符合软盘的大小)当我们试图提取这样的一个文件时,CABSDK会告诉我们含有该文件剩余部分的CAB的名字。 它还会在原始CAB所在的相同目录下寻找那个CAB,如果存在的话则从中提取文件的剩余部分。 因为我们要在视图窗口中标示分块文件,所以 OnListBeginDrag() 会检查拖放的结果,看是否找到了分块文件: LRESULTCMainFrame: : OnListBeginDrag(NMHDR*phdr) { //... //Startthedrag/drop! hr=dropsrc.DoDragDrop(DROPEFFECT_COPY,&dwEffect); if(FAILED(hr)) ATLTRACE("DoDragDrop()failed,error: 0x%08X\n",hr); else { //IfwefoundanyfilescontinuedintootherCABs,updatetheUI. constvector vector : const_iteratorit; for(it=vecResults.begin();it! =vecResults.end();it++) { if(it->bPartialFile) m_view.UpdateContinuedFile(*it); } } return0; } 我们调用 GetDragResults() 来获取反映了拖放操作结果的更新过的 vector 如果结构中的 bPartialFile 成员为 true,则表示该文件仅部分存在于此CAB中。 我们再调用视图方法 UpdateContinuedFile(),并将信息结构传递给它,因而它可以相应地文件列表视图中的项。 下面就是当发现有后续CAB时,应用程
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ATLWTL 第十 部分