简体   繁体   English

将文件从(例如)Windows资源管理器中的文件拖放到WPF应用程序中

[英]Drag'n'drop files from (e.g.) iPhone in Windows Explorer onto a WPF application

I have a C# WPF app that allows users to import files by dragging them in from Windows Explorer and dropping them on the main app window. 我有一个C#WPF应用程序,允许用户通过从Windows资源管理器中拖入文件并将其放在主应用程序窗口中来导入文件。

It works fine when dragging files from physical disks, but when dragging files from a connected device like an iPhone or camera connected via USB, I don't recognise any of the data formats returned by dragEventArgs.Data.GetFormats() in the window's Drop handler. 从物理磁盘拖动文件时它可以正常工作,但是当从连接的设备(如iPhone或通过USB连接的摄像头)拖动文件时,我无法识别窗口的Drop中的dragEventArgs.Data.GetFormats()返回的任何数据格式处理程序。

Anyone care to share some tips or point me at a good example or walkthrough of how to read/import files from a 'virtual' filesystem in C#/.NET in this way? 任何人都喜欢分享一些提示或指出一个很好的例子或演绎如何以这种方式从C#/ .NET中的“虚拟”文件系统读取/导入文件?

Thanks, 谢谢,

Dylan 迪伦

Getting the filenames 获取文件名

Getting the filenames is easy. 获取文件名很简单。 Just call: 只需致电:

dragEventArgs.Data.GetData("FileGroupDescriptorW")

this will return a MemoryStream that contains a FILEGROUPDESCRIPTORA structure. 这将返回包含FILEGROUPDESCRIPTORA结构的MemoryStream This can be parsed to get the filenames. 这可以解析为获取文件名。 Here and here are links to projects on CodeProject that show you two different ways to parse FILEGROUPDESCRIPTORA in C#, so I won't go into detail here. 这里这里是CodeProject项目的链接,它们向您FILEGROUPDESCRIPTORA在C#中解析FILEGROUPDESCRIPTORA两种不同方法,因此我不在这里详细介绍。 I would probably use the technique described in the first project. 我可能会使用第一个项目中描述的技术。

Getting the actual data 获取实际数据

To get the actual data, you use the FileContents format. 要获取实际数据,请使用FileContents格式。 Unfortunately you either have to use reflection to access private methods, or write some COM interop yourself. 不幸的是,您必须使用反射来访问私有方法,或者自己编写一些COM互操作。 The problem is that to get the data you must call the System.Runtime.InteropServices.ComTypes.IDataObject interface with a FORMATETC structure that has lindex set to the item index. 问题是,要获取数据,必须使用设置为项索引的lindex的FORMATETC结构调用System.Runtime.InteropServices.ComTypes.IDataObject接口。 Unfortunately System.Windows.DataObject's implementation always calls it with lindex=-1. 不幸的是,System.Windows.DataObject的实现始终使用lindex = -1调用它。

The easiest solution is probably to use reflection to call private members of WPF's DataObject . 最简单的解决方案可能是使用反射来调用WPF的DataObject私有成员。 Be forewarned, however, that this may break your code in future NET Framework versions. 但是,请注意,在未来的.NET Framework版本中,这可能会破坏您的代码。 If that is completely unacceptable, your other option is to call the RegisterDragDrop function in ole32.dll to register a custom IOleDropTarget , then talk directly to the COM IDataObject that is passed in. This is not terribly difficult, but the reflection solution is much easier and will probably work for many versions of NET Framework, so that is what I will focus on. 如果这是完全不可接受的,你的另一个选择是调用ole32.dllRegisterDragDrop函数来注册一个自定义IOleDropTarget ,然后直接与传入的COM IDataObject对话。这不是非常困难,但反射解决方案要容易得多并且可能适用于许多版本的.NET Framework,因此这是我将关注的内容。

Here is what to do to retrieve a FileContent for a particular index: 以下是检索特定索引的FileContent的方法:

  1. Reflect on the actual class of the data object to find a method called "GetData" that takes four arguments 反映数据对象的实际类,以找到一个带有四个参数的名为“GetData”的方法
  2. If the method was not found, reflect again to find a field of type System.Windows.IDataObject , get its value, and go back to step 1 (recursion is safe here) 如果找不到该方法,请再次反映以查找System.Windows.IDataObject类型的字段,获取其值,然后返回步骤1(此处递归是安全的)
  3. Use MethodInfo.Invoke to call the "GetData" you found with arguments: "FileContents", false, ComTypes.DVASPECT, lindex 使用MethodInfo.Invoke调用您使用参数找到的“GetData”:“FileContents”,false,ComTypes.DVASPECT,lindex
  4. Read the file data from the returned MemoryStream 从返回的MemoryStream读取文件数据

Here is the gist of the code to retrieve the file contents for a given index: 以下是检索给定索引的文件内容的代码要点:

public MemoryStream GetFileContents(IDataObject dataObject, int index)
{
  MethodInfo getData = (
    from method in dataObject.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
    where method.Name=="GetData" && method.GetParameters().Length==4
    select method
  ).FirstOrDefault();

  if(getData==null)
  {
    FieldInfo innerField = (
      from field in dataObject.GetType().GetFields()
      where field.FieldType == typeof(IDataObject)
      select field
    ).FirstOrDefault();
    if(innerField==null) throw new Exception("Cannot get FileContents from DataObject of type" + dataObject.GetType());
    return GetFileContents((IDataObject)innerField.GetValue(dataObject), index);
  }

  return (MemoryStream)getData.Invoke(dataObject, new object[]
  {
    "FileContents", false,
    System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT,
    index
  });
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM