【问题标题】:How can I defer the "rendering" of my DataObject during Winforms cross process drag/drop如何在 Winforms 跨进程拖放期间推迟 DataObject 的“渲染”
【发布时间】:2012-03-16 00:22:48
【问题描述】:

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

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

有什么方法可以确保 GetData() 仅在放置时发生?或者,在 Winforms 程序中实现这种延迟删除机制的正确机制是什么?

【问题讨论】:

    标签: winforms drag-and-drop ole


    【解决方案1】:

    经过一些研究,我能够弄清楚如何做到这一点,而无需实现 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 复制数据的良好效果。显然,在处理 COM IStream 和 .NET System.IO.Stream 之间的映射的 .NET 互操作层中正在发生一些魔法。

    我现在要做的就是编写一个类,将流的创建推迟到最后一分钟 (Lazy object pattern),此时放置目标开始调用 LengthRead 等。看起来像这:(为简洁而编辑的部分)

    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 方法的行为不端的放置目标会导致代价高昂的操作提前发生。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 2020-09-03
      • 2015-11-22
      • 2018-07-12
      • 1970-01-01
      • 2019-10-11
      相关资源
      最近更新 更多