После некоторых исследований я смог выяснить, как это сделать, не прибегая к реализации интерфейса COM IDataObject
(со всеми его FORMATETC
Gunk).Я подумал, что это может заинтересовать других в том же затруднительном положении, поэтому я написал свое решение.Если это можно сделать более умно, я весь глаз / уши!
У класса System.Windows.Forms.DataObject
есть этот конструктор:
public DataObject(string format, object data)
Я назвал это так:
string expensive = GenerateStringVerySlowly();
var dataObject = new DataObject(
DataFormats.UnicodeText,
expensive);
DoDragDrop(dataObject, DragDropEffects.Copy);
Приведенный выше код поместит строковые данные в HGLOBAL
во время операции копирования.Однако вы также можете вызвать конструктор следующим образом:
string expensive = GenerateStringVerySlowly();
var dataObject = new DataObject(
DataFormats.UnicodeText,
new MemoryStream(Encoding.Unicode.GetBytes(expensive)));
DoDragDrop(dataObject, DragDropEffects.Copy);
Вместо того, чтобы копировать данные с помощью HGLOBAL
, этот более поздний вызов имеет приятный эффект копирования данных с помощью (COM) IStream
,По-видимому, происходит некоторая магия в слое взаимодействия .NET, который обрабатывает сопоставление между COM IStream
и .NET System.IO.Stream
.
Все, что мне теперь нужно было сделать, - это написать класс, который отложил созданиепоток до самой последней минуты ( шаблон Ленивый объект ), когда цель отбрасывания начинает вызывать Length
, Read
и т. д. Это выглядит так: (части, отредактированные для краткости)
public class DeferredStream : Stream
{
private Func<string> generator;
private Stream stm;
public DeferredStream(Func<string> expensiveGenerator)
{
this.generator = expensiveGenerator;
}
private Stream EnsureStream()
{
if (stm == null)
stm = new MemoryStream(Encoding.Unicode.GetBytes(generator()));
return stm;
}
public override long Length
{
get { return EnsureStream().Length; }
}
public override long Position
{
get { return EnsureStream().Position; }
set { EnsureStream().Position = value; }
}
public override int Read(byte[] buffer, int offset, int count)
{
return EnsureStream().Read(buffer, offset, count);
}
// Remaining Stream methods elided for brevity.
}
Обратите внимание, что дорогостоящие данные генерируются только при первом вызове метода EnsureStream
.Это не произойдет, пока цель отбрасывания не захочет высосать данные в IStream
.Наконец, я изменил код вызова на:
var dataObject = new DataObject(
DataFormats.UnicodeText,
new DeferredStream(GenerateStringVerySlowly));
DoDragDrop(dataObject, DragDropEffects.Copy);
Это было именно то, что мне было нужно, чтобы эта работа работала.Тем не менее, я полагаюсь на хорошее поведение цели отбрасывания здесь.Неправильное поведение целей отбрасывания, которые с нетерпением вызывают, скажем, метод Read
, скажем, приведет к тому, что дорогостоящая операция произойдет раньше.