2012-03-16 1 views
4

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

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

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

ответ

3

После некоторого исследования я смог выяснить, как это сделать, не используя COM-интерфейс IDataObject (со всеми его галочками FORMATETC). Я думал, что это может представлять интерес для других в том же затруднительном положении, поэтому я написал свое решение. Если это можно сделать более умно, я все глаза/уши!

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, 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, скажем, вызовет дорогостоящую операцию раньше.

Смежные вопросы