I have an object which, although it has a text representation (i.e. could be stored in a string of about 1000 printable characters), is expensive to generate. I also have a tree control which shows “summaries” of the objects. I want to drag/drop these objects not only within my own application, but also to other applications that accept CF_TEXT or CF_UNICODETEXT, at which point the textual representation is inserted into the drop target.
I’ve been thinking of delaying the “rendering” the text representation of my object so that it only takes place when the object is dropped or pasted. However, it seems that Winforms is eagerly calling the GetData() method at the start of the drag, which causes a painful multi-second delay at the start of the drag.
Is there any way ensure that the GetData() happens only at drop time? Alternatively, what is the right mechanism for implementing this deferred drop mechanism in a Winforms program?
After some research, I was able to figure out how to do this without having to implement the COM interface
IDataObject(with all of itsFORMATETCgunk). I thought it might be of interest to others in the same quandary, so I’ve written up my solution. If it can be done more cleverly, I’m all eyes/ears!The
System.Windows.Forms.DataObjectclass has this constructor:I was calling it like this:
The code above will put the string data into an
HGLOBALduring the copy operation. However, you can also call the constructor like this:Rather than copying the data via an
HGLOBAL, this later call has the nice effect of copying the data via a (COM)IStream. Apparently some magic is going on in the .NET interop layer that handles mapping between COMIStreamand .NETSystem.IO.Stream.All I had to do now was to write a class that deferred the creation of the stream until the very last minute (Lazy object pattern), when the drop target starts calling
Length,Readetc. It looks like this: (parts edited for brevity)Note that the expensive data is only generated when the
EnsureStreammethod is called for the first time. This doesn’t happen until the drop target starts wanting to suck down the data in theIStream. Finally, I changed the calling code to:This was exactly what I needed to make this work. However, I am relying on the good behaviour of the drop target here. Misbehaving drop targets that eagerly call, say, the
Readmethod, say, will cause the expensive operation to happen earlier.