我有一个对象,虽然它有一个文本表示(即可以存储在大约1000个可打印字符的字符串中),但生成起来很昂贵。我还有一个树形控件,显示对象的“摘要”。我想将这些对象不仅拖放到我自己的应用程序中,而且拖放到接受CF_TEXT或CF_UNICODETEXT的其他应用程序,此时文本表示将插入到放置目标中。
我一直在考虑延迟“渲染”我的对象的文本表示,以便它只在删除或粘贴对象时发生。但是,似乎Winforms在拖动开始时急切地调用GetData()方法,这会在拖动开始时导致痛苦的多秒延迟。
有没有办法确保GetData()仅在丢弃时发生?或者,在Winforms程序中实现这种延迟丢弃机制的正确机制是什么?
答案 0 :(得分:3)
经过一些研究,我能够弄清楚如何在不必实现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
之间映射的魔法。
现在我所要做的就是编写一个类,该类延迟创建流,直到最后一分钟(Lazy object pattern),当放置目标开始调用Length
时,{{1}它看起来像这样:(部分编辑为了简洁)
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);
方法急切地调用行为不当的下降目标会导致昂贵的操作更早发生。