Merging/converting 虚拟文件数据对象和 System.Windows.Forms.DataObject
Merging/converting VirtualFileDataObject and System.Windows.Forms.DataObject
我正在尝试在拖放操作期间以多种格式从数据库传递文件:
- 如果拖拽发生在同一个进程内,只传文件ID;目标控件将知道如何处理它
- 如果拖动发生在不同的应用程序上,则在拖放时传递一个流来下载文件
前者用DataObject
就够简单了,后者用VirtualFileDataObject
就可以完成。然而,VirtualFileDataObject
没有像 DataObject
s 那样方便的 API,我不确定如何将相同的信息传递给它(或者它是否可能)。
具体前者我是这样操作的:
[Serializable]
private class FileDragDropInfo
{
public int FileID { get; set; }
public string FileName { get; set; }
}
[Serializable]
private class FileDragDropInfoArray
{
public FileDragDropInfo[] Files { get; set; }
}
[..]
var data = new DataObject();
data.SetData(new FileDragDropInfoArray { .Files = items.ToArray() });
而后者如下:
var vData = new VirtualFileDataObject.VirtualFileDataObject();
vData.SetData(items.Select(i => new VirtualFileDataObject.VirtualFileDataObject.FileDescriptor
{
Name = i.FileName,
StreamContents = (s) => { /* download the stream here */ }
}));
这两个单独工作。
但是,VirtualFileDataObject
不幸的是缺少像 GetData()
这样的方法来将该格式添加回原来的 DataObject
。相反,VirtualFileDataObject
也(与 DataObject
不同)没有执行转换的重载。
但是,它确实有这个过载:
public void SetData(short dataFormat, IEnumerable<byte> data);
我猜第一个参数与 System.Windows.Forms.DataFormats.Format 中的 Id
属性 相同,因此第一部分可能有效:
foreach (var format in data.GetFormats().Select(f => System.Windows.Forms.DataFormats.GetFormat(f))
{
vData.SetData(format.Id, /* how do I pass the data? */);
}
但是,浏览了 System.Windows.Forms.DataObject 的源代码后,我不知道它的 conversion/serialization 传递数据的发生位置和方式,以便我可以将它作为第二个参数传递一个byte[]
。 SaveDataToHandle()
和整个 DataStore
class 等相关代码也是私有的,因此我无法直接调用它们(除非反射)。
我实际上没有尝试过如果我需要做的就是传递这样的句柄。我在正确的轨道上吗?
将其他格式传递给 VirtualFileDataObject
是否 完全有效 ,或者这两者根本不兼容? (或者我必须扩展 VirtualFileDataObject
来支持这个吗?)
我弄清楚了,至少 DataFormats.Serializable
,现在将其作为扩展方法 ImportDataObject
实现:
public static class VirtualFileDataObjectExtensions
{
public static void ImportDataObject(this ref VirtualFileDataObject.VirtualFileDataObject virtualFileDataObject, DataObject dataObject)
{
if (virtualFileDataObject == null)
throw new ArgumentNullException(nameof(virtualFileDataObject));
if (dataObject == null)
throw new ArgumentNullException(nameof(dataObject));
foreach (var format in dataObject.GetFormats())
{
short formatIDShort = _GetShortFormatID(format);
var data = dataObject.GetData(format);
// we only support Serializable for now
if (format.Equals(DataFormats.Serializable) || data is ISerializable || data?.GetType().IsSerializable)
virtualFileDataObject.SetData(formatIDShort, _SerializeDataObject(data));
}
}
private static short _GetShortFormatID(string format)
{
// unfortunately, .NET uses an int, but ushort would be correct
ushort formatID = System.Convert.ToUInt16(DataFormats.GetFormat(format).Id & 0xFFFF);
short formatIDShort;
// and VirtualFileDataProvider takes a short instead of a ushort
unchecked { formatIDShort = (short)formatID; }
return formatIDShort;
}
private readonly static byte[] _serializedObjectID = new Guid(0xFD9EA796, 0x3B13, 0x4370, 0xA6, 0x79, 0x56, 0x10, 0x6B, 0xB2, 0x88, 0xFB).ToByteArray();
private static byte[] _SerializeDataObject(object data)
{
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter binaryWriter = new BinaryWriter(ms))
{
binaryWriter.Write(_serializedObjectID);
var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
formatter.Serialize(ms, data);
}
ms.FlushAsync();
return ms.ToArray();
}
}
}
此代码可能需要一些清理,但对我来说非常有用。
我正在尝试在拖放操作期间以多种格式从数据库传递文件:
- 如果拖拽发生在同一个进程内,只传文件ID;目标控件将知道如何处理它
- 如果拖动发生在不同的应用程序上,则在拖放时传递一个流来下载文件
前者用DataObject
就够简单了,后者用VirtualFileDataObject
就可以完成。然而,VirtualFileDataObject
没有像 DataObject
s 那样方便的 API,我不确定如何将相同的信息传递给它(或者它是否可能)。
具体前者我是这样操作的:
[Serializable]
private class FileDragDropInfo
{
public int FileID { get; set; }
public string FileName { get; set; }
}
[Serializable]
private class FileDragDropInfoArray
{
public FileDragDropInfo[] Files { get; set; }
}
[..]
var data = new DataObject();
data.SetData(new FileDragDropInfoArray { .Files = items.ToArray() });
而后者如下:
var vData = new VirtualFileDataObject.VirtualFileDataObject();
vData.SetData(items.Select(i => new VirtualFileDataObject.VirtualFileDataObject.FileDescriptor
{
Name = i.FileName,
StreamContents = (s) => { /* download the stream here */ }
}));
这两个单独工作。
但是,VirtualFileDataObject
不幸的是缺少像 GetData()
这样的方法来将该格式添加回原来的 DataObject
。相反,VirtualFileDataObject
也(与 DataObject
不同)没有执行转换的重载。
但是,它确实有这个过载:
public void SetData(short dataFormat, IEnumerable<byte> data);
我猜第一个参数与 System.Windows.Forms.DataFormats.Format 中的 Id
属性 相同,因此第一部分可能有效:
foreach (var format in data.GetFormats().Select(f => System.Windows.Forms.DataFormats.GetFormat(f))
{
vData.SetData(format.Id, /* how do I pass the data? */);
}
但是,浏览了 System.Windows.Forms.DataObject 的源代码后,我不知道它的 conversion/serialization 传递数据的发生位置和方式,以便我可以将它作为第二个参数传递一个byte[]
。 SaveDataToHandle()
和整个 DataStore
class 等相关代码也是私有的,因此我无法直接调用它们(除非反射)。
我实际上没有尝试过如果我需要做的就是传递这样的句柄。我在正确的轨道上吗?
将其他格式传递给 VirtualFileDataObject
是否 完全有效 ,或者这两者根本不兼容? (或者我必须扩展 VirtualFileDataObject
来支持这个吗?)
我弄清楚了,至少 DataFormats.Serializable
,现在将其作为扩展方法 ImportDataObject
实现:
public static class VirtualFileDataObjectExtensions
{
public static void ImportDataObject(this ref VirtualFileDataObject.VirtualFileDataObject virtualFileDataObject, DataObject dataObject)
{
if (virtualFileDataObject == null)
throw new ArgumentNullException(nameof(virtualFileDataObject));
if (dataObject == null)
throw new ArgumentNullException(nameof(dataObject));
foreach (var format in dataObject.GetFormats())
{
short formatIDShort = _GetShortFormatID(format);
var data = dataObject.GetData(format);
// we only support Serializable for now
if (format.Equals(DataFormats.Serializable) || data is ISerializable || data?.GetType().IsSerializable)
virtualFileDataObject.SetData(formatIDShort, _SerializeDataObject(data));
}
}
private static short _GetShortFormatID(string format)
{
// unfortunately, .NET uses an int, but ushort would be correct
ushort formatID = System.Convert.ToUInt16(DataFormats.GetFormat(format).Id & 0xFFFF);
short formatIDShort;
// and VirtualFileDataProvider takes a short instead of a ushort
unchecked { formatIDShort = (short)formatID; }
return formatIDShort;
}
private readonly static byte[] _serializedObjectID = new Guid(0xFD9EA796, 0x3B13, 0x4370, 0xA6, 0x79, 0x56, 0x10, 0x6B, 0xB2, 0x88, 0xFB).ToByteArray();
private static byte[] _SerializeDataObject(object data)
{
using (MemoryStream ms = new MemoryStream())
{
using (BinaryWriter binaryWriter = new BinaryWriter(ms))
{
binaryWriter.Write(_serializedObjectID);
var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
formatter.Serialize(ms, data);
}
ms.FlushAsync();
return ms.ToArray();
}
}
}
此代码可能需要一些清理,但对我来说非常有用。