合并/转换VirtualFileDataObject和System.Windows.Forms.DataObject

时间:2018-07-30 10:34:03

标签: .net-4.6

我正在尝试通过拖放操作从数据库传递文件,并采用多种格式:

  • 如果拖动在同一过程中发生,则只需传递文件ID;目标控件将知道如何处理
  • 如果拖动发生在其他应用程序上,请在拖放时传递流以下载文件

前者对于DataObject很简单,而后者可以使用VirtualFileDataObject完成。但是,VirtualFileDataObject所提供的API不如DataObject那样方便,而且我不确定如何将相同的信息传递给它(或者是否完全有可能)。

具体来说,我的做法如下:

[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的源代码之后,我不知所措,它在哪里以及如何对传递的数据进行转换/序列化,以便我可以将其作为第二个参数作为{ {1}}。相关代码(例如byte[]和整个SaveDataToHandle()类也是私有的,因此我将无法直接调用它们(除非反射)。

如果我需要做的只是传递这样的句柄,我实际上并没有尝试过。我在正确的轨道上吗?

将其他格式传递给DataStore会完全有效吗?还是两者根本不兼容? (或者我必须扩展VirtualFileDataObject来支持这一点吗?)

1 个答案:

答案 0 :(得分:1)

我至少在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();
        }
    }
}

此代码可能需要进行一些清理,但对我而言非常有用。