ATLWTL第十部分.docx

上传人:b****8 文档编号:10909570 上传时间:2023-02-23 格式:DOCX 页数:31 大小:569.68KB
下载 相关 举报
ATLWTL第十部分.docx_第1页
第1页 / 共31页
ATLWTL第十部分.docx_第2页
第2页 / 共31页
ATLWTL第十部分.docx_第3页
第3页 / 共31页
ATLWTL第十部分.docx_第4页
第4页 / 共31页
ATLWTL第十部分.docx_第5页
第5页 / 共31页
点击查看更多>>
下载资源
资源描述

ATLWTL第十部分.docx

《ATLWTL第十部分.docx》由会员分享,可在线阅读,更多相关《ATLWTL第十部分.docx(31页珍藏版)》请在冰豆网上搜索。

ATLWTL第十部分.docx

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&vec);

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&GetDragResults();

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)

{

vectorvec;

CComObjectStackdropsrc;

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,我们要用它来初始化 CDragDropSource 对象。

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&vecResults=dropsrc.GetDragResults();

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时,应用程

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 小学作文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1