如何在Winforms跨进程拖放期间推迟我的DataObject的“渲染”

时间:2012-03-16 00:22:48

标签: winforms drag-and-drop ole

我有一个对象,虽然它有一个文本表示(即可以存储在大约1000个可打印字符的字符串中),但生成起来很昂贵。我还有一个树形控件,显示对象的“摘要”。我想将这些对象不仅拖放到我自己的应用程序中,而且拖放到接受CF_TEXT或CF_UNICODETEXT的其他应用程序,此时文本表示将插入到放置目标中。

我一直在考虑延迟“渲染”我的对象的文本表示,以便它只在删除或粘贴对象时发生。但是,似乎Winforms在拖动开始时急切地调用GetData()方法,这会在拖动开始时导致痛苦的多秒延迟。

有没有办法确保GetData()仅在丢弃时发生?或者,在Winforms程序中实现这种延迟丢弃机制的正确机制是什么?

1 个答案:

答案 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); 方法急切地调用行为不当的下降目标会导致昂贵的操作更早发生。