如何将程序中的内容“拖”到桌面或文件夹中(DoDragDrop高难度高分问题)

chenybin 2007-01-30 11:30:17
实现拖出,

现在程序的数据源是Listview,具体的内容根据Listview选中的文件从ftp下载(这个无所谓,反正从其他地方来的,数据库的stream等等都可以,不同于普通文本拖放),希望拖放完毕,鼠标放开以后开始下载ftp的内容,并保存到本地,

(从程序窗口中的listview拖到桌面等地方)

不想用控件,主要想知道原理,如果哪位兄弟帮我把代码从控件中分离出来夜行,非常感谢
...全文
954 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
SonicX 2007-01-31
  • 打赏
  • 举报
回复
转变下思路吧
在GetData过程做点改动,比如弄个文件进度条什么的,后台下载数据咯
chenybin 2007-01-31
  • 打赏
  • 举报
回复
是的,不过会有个小问题,就是如果网络不是很好,我们在DoDragDrop前准备数据这个过程会很漫长,这样不太好
SonicX 2007-01-31
  • 打赏
  • 举报
回复
根据MSDN说法就是先将文件存到硬盘上再进行拖拽,拖拽文件没有内存操作方式
你可以看下其他软件的拖拽,都是将文件先存到TEMP目录的,比如WINRAR,FOXMAIL...
chenybin 2007-01-31
  • 打赏
  • 举报
回复
谢谢楼上两位兄弟,dabaicai(小老头) 思路可以考虑,不知道算不算“正统”的做法

谢谢

to SonicX(SonicX),现在的问题是文件其实并不存在本地

楼下继续
dabaicai 2007-01-31
  • 打赏
  • 举报
回复
事实上你拖拽的时候是先已经选定好了的,比如从A拖到B里面,B里面显示的只是A里面选中的东西(桌面也是一个文件夹),而实际上是开了线程开始下载(处理)的
SonicX 2007-01-31
  • 打赏
  • 举报
回复
拖放要放在OLE对象里涉及到的API
DoDragDrop

DoDragDrop需要用到IDataObject,IDropSource
这个DELPHI没有申明需要自己做,过程要根据需要自己写了
关键过程为
GetData 向目标窗口传送数据
QueryGetData 目标窗口查询数据接口类型
其他可以直接Result := E_NOTIMPL;表示为接口不支持
还有个SetData接口,这个是将OLE数据存入IDataObject类,因为过程是自己写的,可以直接使用程序中的公共变量数据,不用通过SetData将数据写入IDataObject类

uses ShlObj,ActiveX, ComObj;
Tform1 = class(TForm, IDropSource)
....
....
Pubilc
//IDropSource
function QueryContinueDrag(fEscapePressed: BOOL; grfKeyState: Longint): HResult; stdcall;
function GiveFeedback(dwEffect: Longint): HResult; stdcall;
end;

TDataObject = class(TInterfacedObject,IDataObject)
public
constructor Create;
procedure Free;

function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult; stdcall;
function GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium): HResult; stdcall;
function QueryGetData(const formatetc: TFormatEtc): HResult; stdcall;
function GetCanonicalFormatEtc(const formatetc: TFormatEtc; out formatetcOut: TFormatEtc): HResult;stdcall;
function SetData(const formatetc: TFormatEtc; var medium: TStgMedium; fRelease: BOOL): HResult; stdcall;
function EnumFormatEtc(dwDirection: Longint; out enumFormatEtc: IEnumFormatEtc): HResult; stdcall;
function DAdvise(const formatetc: TFormatEtc; advf: Longint; const advSink: IAdviseSink; out dwConnection: Longint): HResult; stdcall;
function DUnadvise(dwConnection: Longint): HResult; stdcall;
function EnumDAdvise(out enumAdvise: IEnumStatData): HResult; stdcall;
end;

function Tform1.QueryContinueDrag(fEscapePressed: BOOL;
grfKeyState: Longint): HResult; stdcall;
begin
if fEscapePressed or (grfKeyState and MK_RBUTTON = MK_RBUTTON) then
Result := DRAGDROP_S_CANCEL
else
Result := DRAGDROP_S_DROP;
end;
function Tform1.GiveFeedback(dwEffect: Longint): HResult; stdcall;
begin
Result := DRAGDROP_S_USEDEFAULTCURSORS;
end;

//QueryGetData过程
function TDataObject.QueryGetData(const formatetc: TFormatEtc): HResult; stdcall;
begin
Result := DV_E_FORMATETC; //不支持的格式

if (formatetc.cfFormat=CF_HDROP) and //表示支持文件拖拽格式
(formatetc.tymed=TYMED_HGLOBAL ) and
(formatetc.dwAspect=DVASPECT_CONTENT) then
Result := S_OK;
end;

//GetData过程
function TDataObject.GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium): HResult; stdcall;
var
BufferText : String;
pGlobal : Pointer;
begin
Result := DV_E_FORMATETC; //不支持的格式
if not (Self.QueryGetData(formatetcIn)=S_OK) then exit;
FillChar(Medium,Sizeof(TStgMedium),0);
Medium.tymed:=formatetcIn.tymed;
BufferText:='c:\temp\aa.txt'+#0+#0;
//需要拖拽的文件,多个的如下
//'c:\temp\aa.txt'+#0+#0+'c:\temp\aa.txt'+#0+#0;+'c:\temp\aa.txt'+#0+#0;
Medium.hGlobal := GlobalAlloc(GMEM_ZEROINIT or GMEM_MOVEABLE or GMEM_SHARE, Length(BufferText)+1+Sizeof(TDropFiles));
pGlobal := GlobalLock(Medium.hGlobal);
PDropFiles(pGlobal)^.pFiles:=Sizeof(TDropFiles);
PDropFiles(pGlobal)^.pt:=Point(0,0);
PDropFiles(pGlobal)^.fNC:=False;
PDropFiles(pGlobal)^.fWide:=False;
inc(Longword(pGlobal),Sizeof(TDropFiles)); //指针后移
CopyMemory(PGlobal,Pchar(BufferText),Length(BufferText)+1);
GlobalUnlock(Medium.hGlobal);
Medium.unkForRelease := nil;
Result := S_OK;
end;


//////////////
在需要拖拽的时候
var
DataObject : TDataObject;
begin
DataObject:=TDataObject.Create;
Effect := DROPEFFECT_NONE;
try
DoDragDrop(DataObject, Self, DROPEFFECT_MOVE, Effect);
except
end;
DataObject.Free;
end;
chenybin 2007-01-31
  • 打赏
  • 举报
回复
因为用户体验来说,比较合理的感觉是拖只有数据才下载,而不是拖之前就下载了
SonicX 2007-01-31
  • 打赏
  • 举报
回复
拖拽和接收都是自己开发的程序用内存流什么的肯定没问题,如果拖到桌面或别的程序貌似只能按照公共的操作方式来进行
chenybin 2007-01-31
  • 打赏
  • 举报
回复
看来一个简单的办法就是先建立一个很小的同名临时文件,鼠标放开以后在真正开始下载,并填充文件。

msdn的例子是间建立临时文件,不知道有没有机制可以在DoDragDrop只有调用我们的方法来处理数据获取。比如类型为TYPED_ISTREAM等等,一直没有好的资料和示例
chenybin 2007-01-30
  • 打赏
  • 举报
回复
http://community.csdn.net/Expert/topic/5319/5319586.xml?temp=6.657046E-02
using System; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; namespace WindowsFormsApplication1 { class DragItemListView:ListView { public DragItemListView() { SetStyle(ControlStyles.DoubleBuffer | ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); UpdateStyles(); this.MultiSelect = false; this.ListViewItemSorter = new ListViewIndexComparer(); if (OSFeature.Feature.IsPresent(OSFeature.Themes)) { this.AllowDrop = true; this.ItemDrag += new ItemDragEventHandler(DragItemListView_ItemDrag); this.DragEnter += new DragEventHandler(DragItemListView_DragEnter); this.DragLeave += new EventHandler(DragItemListView_DragLeave); this.DragOver += new DragEventHandler(DragItemListView_DragOver); this.DragDrop += new DragEventHandler(DragItemListView_DragDrop); } } void DragItemListView_DragDrop(object sender, DragEventArgs e) { // Retrieve the index of the insertion mark; int targetIndex = this.InsertionMark.Index; // If the insertion mark is not visible, exit the method. if (targetIndex == -1) { return; } // If the insertion mark is to the right of the item with // the corresponding index, increment the target index. // Retrieve the dragged item. ListViewItem draggedItem = (ListViewItem)e.Data.GetData(typeof(ListViewItem)); // Insert a copy of the dragged item at the target index. // A copy must be inserted before the original item is removed // to preserve item index values. ListViewItem NewItem = (ListViewItem)draggedItem.Clone(); if (draggedItem.Index < targetIndex) { if (targetIndex - draggedItem.Index > 1) NewItem.Group = this.Items[targetIndex - 1].Group; else NewItem.Group = this.Items[targetIndex].Group; } else { if (draggedItem.Index - targetIndex > 1) NewItem.Group = this.Items[targetIndex].Group; else NewItem.Group = this.Items[targetIndex - 1].Group; } if (this.InsertionMark.AppearsAfterItem) { targetIndex++; } this.Items.Insert(targetIndex, NewItem); // Remove the original copy of the dragged item. this.Items.Remove(draggedItem); } void DragItemListView_DragOver(object sender, DragEventArgs e) { // Retrieve the client coordinates of the mouse pointer. Point targetPoint = this.PointToClient(new Point(e.X, e.Y)); // Retrieve the index of the item closest to the mouse pointer. //int targetIndex = this.InsertionMark.NearestIndex(targetPoint); int targetIndex = GetNearItem(targetPoint).Index; // Confirm that the mouse pointer is not over the dragged item. if (targetIndex > -1) { // Determine whether the mouse pointer is to the left or // the right of the midpoint of the closest item and set // the InsertionMark.AppearsAfterItem property accordingly. Rectangle itemBounds = this.GetItemRect(targetIndex); if (targetPoint.X > itemBounds.Left + (itemBounds.Width / 2)) { this.InsertionMark.AppearsAfterItem = true; } else { this.InsertionMark.AppearsAfterItem = false; } } // Set the location of the insertion mark. If the mouse is // over the dragged item, the targetIndex value is -1 and // the insertion mark disappears. this.InsertionMark.Index = targetIndex; } void DragItemListView_DragLeave(object sender, EventArgs e) { this.InsertionMark.Index = -1; } void DragItemListView_ItemDrag(object sender, ItemDragEventArgs e) { this.DoDragDrop(e.Item, DragDropEffects.Move); } void DragItemListView_DragEnter(object sender, DragEventArgs e) { e.Effect = e.AllowedEffect; } private class ListViewIndexComparer : System.Collections.IComparer { public int Compare(object x, object y) { return ((ListViewItem)x).Index - ((ListViewItem)y).Index; } } /// /// 搜索最近的ListViewItem /// /// 工作区坐标 /// public ListViewItem GetNearItem(Point ClientPoint) { ListViewItem lvt = this.GetItemAt(ClientPoint.X, ClientPoint.Y); if (lvt != null) return lvt; List vt = new List(); for (int i = 1; i < 10; i++) { lvt = this.GetItemAt(ClientPoint.X, ClientPoint.Y + i); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X, ClientPoint.Y - i); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X + i, ClientPoint.Y); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X + i, ClientPoint.Y + i); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X + i, ClientPoint.Y - i); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X - i, ClientPoint.Y); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X - i, ClientPoint.Y + i); if (lvt != null) vt.Add(lvt); lvt = this.GetItemAt(ClientPoint.X - i, ClientPoint.Y - i); if (lvt != null) vt.Add(lvt); if (vt.Count > 0) break; } if (vt.Count == 0) { return null; } else if (vt.Count == 1) { return vt[0]; } else { double HisDis = 0; int i = 0; foreach (ListViewItem item in vt) { double dis = GetDistince(ClientPoint, item.Position); if (i == 0 || dis < HisDis) { lvt = item; HisDis = dis; } i++; } return lvt; } } /// /// 两点间的距离 /// /// 起点 /// 终点 /// private double GetDistince(Point start, Point end) { double distince2 = Math.Pow((start.X - end.X), 2) + Math.Pow((start.Y - end.Y), 2); return Math.Abs(Math.Sqrt(distince2)); } } }

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧