异步拖放或从外部服务器/设备拖放
从外部服务器或设备异步拖放文件。
引言
本文介绍如何从 Windows 应用程序中的 ListView
拖动来自外部设备或服务器的文件,这些文件未存储在可访问的计算机上。 它提供了一个类,该类中的文件在拖放操作时首先下载,然后再移动。
背景
我编写了一个 Windows 应用程序,它在 ListView
中显示外部设备上的文件。 要求是将文件通过拖放从设备移动到本地计算机。 我的第一次尝试是,在 ListView_ItemDrag
事件中调用 DoDragDrop
方法之前,从设备下载所有选定的文件。 对于一两个小文件来说,这是成功的,但对于更多或更大的文件来说,则不然。 在移动较大或更多文件时,下载速度比用户按下鼠标键的速度慢。 在这种情况下,拖放操作被 Windows 取消。
我在网上找到了两个使用大量 COMInterop 代码的示例。 我认为这必须更容易。
Using the Code
首先,编写一个像我的 FileDragDropHelper
这样的类。 该调用必须继承自 System.Windows.Forms.DataObject
。 下一步,重写 GetDataPresent
方法。 请不要忘记在您的操作之后调用 base
方法 GetDataPresent
。
public class FileDragDropHelper : DataObject
{
private int _downloadIndex;
private readonly Action<string> _downloadAction;
/// <summary>
/// Initializes a new instance of the <see cref="FileDragDropHelper"/> class.
/// </summary>
/// <param name="downloadAction">The action delegate which handles
/// the download of the source file to the target path on local machine
/// or mapped server path.</param>
public FileDragDropHelper(Action<string> downloadAction)
{
if (downloadAction == null)
throw new ArgumentNullException("downloadAction");
_downloadAction = downloadAction;
}
/// <summary>
/// Determines whether data stored in this
/// <see cref="T:System.Windows.Forms.DataObject"/> is associated with,
/// or can be converted to, the specified format.
/// </summary>
/// <param name="format">The format to check for.
/// See <see cref="T:System.Windows.Forms.DataFormats"/>
/// for predefined formats.</param>
/// <returns>
/// true if data stored in this <see cref="T:System.Windows.Forms.DataObject"/>
/// is associated with, or can be converted to,
/// the specified format; otherwise, false.
/// </returns>
public override bool GetDataPresent(string format)
{
StringCollection fileDropList = GetFileDropList();
if (fileDropList.Count > _downloadIndex)
{
string fileName = Path.GetFileName(fileDropList[_downloadIndex]);
if (string.IsNullOrEmpty(fileName))
return false;
_downloadAction(fileDropList[_downloadIndex]);
_downloadIndex++;
}
return base.GetDataPresent(format);
}
}
第三步是使用我们的新拖放助手。 将 ListView
控件添加到您的 Windows Form 控件。 用您的列表项填充它。 在我的例子中,有两页网页。 将 ListView
的 ItemDrag
事件与 Form-Designer 绑定。 在事件处理程序中,迭代选定的 ListView
项,并将项文本填充到泛型 Dictionary<string, string>
中,其中键是临时文件夹中的目标文件,值是源 Uri。 对于拖放操作,您必须指定目标文件存储在临时文件夹中。 此外,在 StringCollection
对象中填充一些临时 文件。
private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
{
if(listView1.SelectedItems.Count == 0)
return;
StringCollection temporaryFiles = new StringCollection();
IDictionary<string, string> sourceFiles =
new Dictionary<string, string>(listView1.SelectedItems.Count);
foreach (ListViewItem item in listView1.SelectedItems)
{
if (string.IsNullOrWhiteSpace(item.Text))
continue;
string fileName = Path.GetFileName(item.Text);
if(string.IsNullOrWhiteSpace(fileName))
continue;
// Create a temporary file with the source file name in the temp folder.
// Also required for the Windows Drag 'n Drop engine.
string targetFilePath = Path.Combine(Path.GetTempPath(), fileName);
temporaryFiles.Add(targetFilePath);
// Required!
// Add the source file to the source files list.
// The source files list is needed for file download.
sourceFiles.Add(targetFilePath, item.Text);
}
FileDragDropHelper data = new FileDragDropHelper(file =>
{
// This lamda expression is equals with a method MethodName
// (string file) {// do something}
KeyValuePair<string, string> filePair = sourceFiles.FirstOrDefault
(fi => string.Equals(fi.Key, file));
if (string.IsNullOrWhiteSpace(filePair.Key) ||
string.IsNullOrWhiteSpace(filePair.Value))
return;
// Do not download async in this async context
WebRequest request = WebRequest.CreateDefault(new Uri(filePair.Value));
using(WebResponse response = request.GetResponse())
{
// read response stream
using(Stream readStream = response.GetResponseStream())
{
if (readStream == null || !readStream.CanRead)
return;
// write stream to file of the current temporary files
using(FileStream writeStream = new FileStream
(filePair.Key, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
byte[] buffer = new byte[16384];
for (int index = readStream.Read(buffer, 0, buffer.Length);
index > 0; index = readStream.Read(buffer, 0, buffer.Length))
writeStream.Write(buffer, 0, index);
}
}
}
});
data.SetFileDropList(temporaryFiles);
DoDragDrop(data, DragDropEffects.Copy);
}
关注点
FileDragDropHelper
类中的 GetDataPresent
方法是成功的关键。
历史
- 2011 年 11 月 19 日:首次发布