Как я могу отложить «рендеринг» моего объекта DataObject во время кросс-процесса перетаскивания Winforms - PullRequest
4 голосов
/ 16 марта 2012

У меня есть объект, который, хотя и имеет текстовое представление (т. Е. Может храниться в строке из около 1000 печатаемых символов), генерировать дорого.У меня также есть элемент управления деревом, который показывает «сводки» объектов.Я хочу перетащить эти объекты не только в мое собственное приложение, но и в другие приложения, которые принимают CF_TEXT или CF_UNICODETEXT, и в этот момент текстовое представление вставляется в целевой объект.

Я думалзадержки «рендеринга» текстового представления моего объекта так, чтобы оно имело место только тогда, когда объект уронен или вставлен.Однако, похоже, что Winforms охотно вызывает метод GetData () в начале перетаскивания, что вызывает болезненную мультисекундную задержку в начале перетаскивания.

Есть ли способ гарантировать, что GetData() происходит только во время отбрасывания?В качестве альтернативы, каков правильный механизм для реализации этого механизма отложенного удаления в программе Winforms?

1 Ответ

3 голосов
/ 07 октября 2012

После некоторых исследований я смог выяснить, как это сделать, не прибегая к реализации интерфейса 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, скажем, приведет к тому, что дорогостоящая операция произойдет раньше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...