Merging/converting 虚拟文件数据对象和 System.Windows.Forms.DataObject

Merging/converting VirtualFileDataObject and System.Windows.Forms.DataObject

我正在尝试在拖放操作期间以多种格式从数据库传递文件:

前者用DataObject就够简单了,后者用VirtualFileDataObject就可以完成。然而,VirtualFileDataObject 没有像 DataObjects 那样方便的 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();
        }
    }
}

此代码可能需要一些清理,但对我来说非常有用。