TvirtualStringTree之DragDrop

本文介绍在Delphi中使用TvirtualStringTree组件实现文件拖拽功能的过程,包括解决拖拽冲突、处理不同类型的拖拽事件及获取拖入文件的具体路径。

     昨天在项目中需要做一个播放列表管理界面,播放列表中的结点本身要求可以拖动,并且要求播放列表能够支持从资源管理器上拖入FLV文件和播放列表文件。我继续使用了强大的TvirtualStringTree组件,但是在同时支持这两种拖动上犯了难,以前做的程序都是只支持其中一种拖动。

   将TvirtualStringTree组件的dragMode属性设置为dmAutomatic,然后添加onDragOver事件,在事件处理函数中将Accept设置为true,然后运行,嗯很好,结点已经可以拖动了,而且拖动到结点的不同位置,光标处加亮形式是不一样的,也就是可以分别拖到结点正上方、和结点上面、和结点下面,做到这里我就知道结点是已经可以拖动的了。

  然后开始处理文件的拖动,嗯,现成的有个TargetFileDrop组件,好的,拖到界面上去,目标设置为我使用的TvirtualStringTree组件,然后运行。砰……程序挂掉,弹出错误提示:“该窗口已经被注册为了拖放目标”。哇靠,不让我两个一起使用,我郁闷。然后仔细看看这个错误是OleException的一种,心里想我啥时候将TvirtualStringTree组件也注册为ole拖动对象了?看看TvirtualStringTree组件的属性,嗯,看见了属性TreeOptions.MiscOptions.toAcceptOleDrop默认是为true,原来是这个属性,想都不想直接改为false,然后运行。嗯,很好程序没挂。拖一个FLV到组件上面,等等,怎么拖动到指定结点,把鼠标移到任何地方都不加亮,这可真郁闷,因为我拖入文件要能够拖到指定的播放列表上去的。看来TargetFileDrop组件不合要求,pass。然后试一下拖动结点,唉?怎么也没加亮了?难道,难道是?对了,是TreeOptions.MiscOptions.toAcceptOleDrop属性的问题,还是改回true吧。嗯,可以有加亮了。

   继续寻找拖入文件的方法,百度一下,嗯,有个方法是要将组件使用AcceptFileDrop函数注册成为能够接收文件拖放的组件?好方法,那来吧,照着说明添加上代码,然后运行,怎么发现怎么都执行不到消息处理代码里去,都直接执行到组件的OnDragDrop事件里了。唉,使用消息添加文件的方法,又失败……

   继续看,嗯VB的Ole拖放?有么有delphi版本的?没有。没有怎么办,不管了,继续找。嗯,等等,ole拖放?怎么这么熟悉,向上看看toAcceptOleDrop属性,哇这个组件已经支持ole拖放了,看来还是去研究研究TvirtualStringTree的帮助文件吧。找到OnDragDrop事件的帮助文档,讲的不多,但是提到了

  1. If you do not find CF_VIRTUALTREE then the operation has been initiated by another application, e.g. the Explorer (then you will find CF_HDROP or CF_SHELLIDLIST in formats) or Notepad (then you will get CF_TEXT and perhaps CF_UNICODETEXT) etc., depending on the data which is actually dropped.

嗯,看来是可以响应拖入文件的,而且帮助文件里还给了一个示范的代码(郁闷的是这个示范代码只处理 CF_VIRTUALTREE,其它的都没有),那照着搬一下。

 

procedure TTFrmPlayListMgr.vstPlayListMgrDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode); var I, J: Integer; AttachMode: TVTNodeAttachMode; Medium: TStgMedium; Data: Pointer; fomat: tagFORMATETC ; DropHandle: HDROP; path: string; nFileCount: Integer; nBufferLen: Integer; lpszFile: LPSTR; nCount : Cardinal; begin if Length(Formats) > 0 then begin // OLE drag'n drop // If the native tree format is listed then use this and accept the drop, otherwise recject (ignore) it. // It is recommend by Microsoft to order available clipboard formats in decreasing detail richness so // the first best format which we can accept is usually the best format we can get at all. for I := 0 to High(Formats) do begin if Formats[I] = CF_VIRTUALTREE then begin case Mode of dmAbove: AttachMode := amInsertBefore; dmOnNode: AttachMode := amAddChildLast; dmBelow: AttachMode := amInsertAfter; else if Assigned(Source) and (Source is TBaseVirtualTree) and (Sender <> Source) then AttachMode := amInsertBefore else AttachMode := amNowhere; end; // in the case the drop target does an optimized move Effect is set to DROPEFFECT_NONE // to indicate this also to the drag source (so the source doesn't need to take any further action) Sender.ProcessDrop(DataObject, Sender.DropTargetNode, Effect, AttachMode); Break; end; if Formats[i] = CF_HDROP then begin showMessage('CF_HDROP '); end; end else begin // VCL drag'n drop, Effects contains by default both move and copy effect suggestion, // as usual the application has to find out what operation is finally to do Beep; end; end;

 

运行然后拖入文件,嗯有提示了,证明是能够执行到ShowMessage的地方,但是从哪里去找拖入的文件名呢?看参数说明,是可以从DataObject里找到数据,但是鬼知道怎么获取数据啊,百度没有,google没有,唉,看TDropFIletarget自己的源码是怎么处理DataOjbect的,从上面的代码看到DataObject参数传给方法ProcessDrop再传到GetTreeFromDataObject,然后,嗯,有处理代码了。

function TBaseVirtualTree.GetTreeFromDataObject(const DataObject: IDataObject): TBaseVirtualTree; // Returns the owner/sender of the given data object by means of a special clipboard format // or nil if the sender is in another process or no virtual tree at all. var Medium: TStgMedium; Data: PVTReference; begin Result := nil; if Assigned(DataObject) then begin StandardOLEFormat.cfFormat := CF_VTREFERENCE; if DataObject.GetData(StandardOLEFormat, Medium) = S_OK then begin Data := GlobalLock(Medium.hGlobal); if Assigned(Data) then begin if Data.Process = GetCurrentProcessID then Result := Data.Tree; GlobalUnlock(Medium.hGlobal); end; ReleaseStgMedium(Medium); end; end; end;

嗯,照着做吧,但问题只有一个对应着cfFormat = CF_HDROP的数据是什么格式?对GlobaLock返回的指针我怎么处理。结果没有一个地方有说明,MSDN、delphi的Windows SDK帮助文档上对CF_HDROP都没有说明,使用百度对CF_HDROP进行搜索,发现只有对剪切板的一些操作的示范代码,再回去研究下OleDrop的原理,正是将数据存到剪切板中。那么,把对剪切板的操作的代码移植到程序中,好了,一个成功的OnDragDrop事件处理函数出来了。

procedure TTFrmPlayListMgr.vstPlayListMgrDragDrop(Sender: TBaseVirtualTree; Source: TObject; DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState; Pt: TPoint; var Effect: Integer; Mode: TDropMode); var I, J: Integer; AttachMode: TVTNodeAttachMode; Medium: TStgMedium; Data: Pointer; fomat: tagFORMATETC ; DropHandle: HDROP; path: string; nFileCount: Integer; nBufferLen: Integer; lpszFile: LPSTR; nCount : Cardinal; begin if Length(Formats) > 0 then begin // OLE drag'n drop // If the native tree format is listed then use this and accept the drop, otherwise recject (ignore) it. // It is recommend by Microsoft to order available clipboard formats in decreasing detail richness so // the first best format which we can accept is usually the best format we can get at all. for I := 0 to High(Formats) do begin if Formats[I] = CF_VIRTUALTREE then begin case Mode of dmAbove: AttachMode := amInsertBefore; dmOnNode: AttachMode := amAddChildLast; dmBelow: AttachMode := amInsertAfter; else if Assigned(Source) and (Source is TBaseVirtualTree) and (Sender <> Source) then AttachMode := amInsertBefore else AttachMode := amNowhere; end; // in the case the drop target does an optimized move Effect is set to DROPEFFECT_NONE // to indicate this also to the drag source (so the source doesn't need to take any further action) Sender.ProcessDrop(DataObject, Sender.DropTargetNode, Effect, AttachMode); Break; end; if Formats[i] = CF_HDROP then begin if Assigned(DataObject) then begin // Format must later be set. fomat.cfFormat:= CF_HDROP; // No specific target device to render on. fomat.ptd:= nil; // Normal content to render. fomat.dwAspect:= DVASPECT_CONTENT; // No specific page of multipage data (we don't use multipage data by default). fomat.lindex:= -1; // Acceptable storage formats are IStream and global memory. The first is preferred. fomat.tymed:= TYMED_ISTREAM or TYMED_HGLOBAL; //fomat.cfFormat := CF_HDROP; if DataObject.GetData(fomat, Medium) = S_OK then begin Data := GlobalLock(Medium.hGlobal); if Assigned(Data) then begin DropHandle := Integer(Data); nFileCount := DragQueryFile(DropHandle, Cardinal(-1),nil,0); for J := 0 to nFileCount - 1 do begin nBufferLen := DragQueryFile(DropHandle,j,nil,0); // lpszFile := LPSTR(GlobalAlloc(GPTR,nBufferLen+1)); // nCount := DragQueryFile(DropHandle,i,lpszFile,nBufferLen+1); lpszFile := LPSTR(GlobalAlloc(GPTR,255)); nCount := DragQueryFile(DropHandle,j,lpszFile,255); //FIXme operation about lpszFile path := lpszFile; ShowMessage(path); GlobalFree(Cardinal(lpszFile)); end; GlobalUnlock(Medium.hGlobal); end; ReleaseStgMedium(Medium); end; end; end; end; end else begin // VCL drag'n drop, Effects contains by default both move and copy effect suggestion, // as usual the application has to find out what operation is finally to do Beep; end; end;

 

然后运行,拖入一个文件,OK,文件全路径弹出来了。搞定了这一步,下面的就是一个程序逻辑问题了,嘿嘿。

转载于:https://www.cnblogs.com/k1988/archive/2009/04/23/2165736.html

上传容量有限,含packge和source V5.5.2: (10 Nov 2014) * Various improvements regarding code style * Implemented #471: Added emVisibleDueToExpansion and emSelected to TVTExportMode * Fixed issue #488: XE7 packages should depend on one another and use suffix 21 * Fixed issue #462: Undo r636, make VirtualTreesD require VirtualTreesR again * Fixed issue #489 XE2 compiler switch error V5.5.1: (13 Oct 2014) * Fixed issue #479: The style hooks for the VCL styles are now registered for TVirtualStringTree and TVirtualDrawTree instead of TBaseVirtualTree, which makes it easier to use own style hooks in derived classes. * Partial fix for issue #478: The standard VCL property StyleElemets (public in TControl in RAD Studio XE3 and higher) is now supported and published for TVirtualStringTree and TVirtualDrawTree (XE3 and higher). This means you can define if the font and the backgrounbd color is taken from the VCL style or the control's properties. Leaving out seBorder is not yet working well, more work will be necessary. * Fixed issue #473: Return type of GetUtilityImages should be TCustomImageList * Fix for issue #470: VCL Styles and sorting failure * Added missing inherited to CMMouseEnter() * Fixed issue #468: Redundant code in CreateSystemImageSet() * Fixed issue #482: AutoScale() could cause exception during form load. * Added fix for #466: No parent window if column created in constructor * Fixed issue #446: ScrollIntoView does not work properly after applying patch from issue #339 * Improvements for toAlwaysSelectNode option: Selection of next sibling has been improved in case the currently selected node is being removed. * Added missing begin/end-block in MeasureItemHeight() * Improved fix for issue #438: Now correctly initializing member of property TVTColors.UnfocusedColor * Improved fix for issue #447: DoMeasureItem() was called for Node instead of Child. * Minor improvement in appearance of border lines in HTML export. * Fixed issue #480: Warning when compiling Delphi XE2 packages * Fixed #472: Redundant conditions in TVclStyleScrollBarsHook.WMMouseMove * Fixed #476: Simplify TVTDragImage.WillMove() * Fixed issue #485: unit VirtualTrees does not compile with {$TYPEDADDRESS ON}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值