Introduction
At the end of April 2002, Nish and I were trying to figure out some of
.NET's clipboard capabilities. When we had finished the conversation we
decided on writing a two part article, Nish doing
part one on the basics then I
would do part two on some more advanced topics. I was going to write it as
soon as the screensaver competition was over, needless to say I forgot....until
now that is.
In this article I hope to cover multiple data formats, and creating your own
custom data formats.
Multiple data formats
Placing multiple data formats on the clipboard is incredibly easy; once you
know the trick :-)
The first tric...er step is to figure out what data formats you will be using.
Later I'll discuss custom formats so ignore those for now. To help you in
your search the DataFormats
class has a list of commonly used
formats stored as static string fields; refer to MSDN for the specifics on each format listed there.
Now that you know the formats you will use, create a instance of a class that
implements IDataObject
, the only such class is (appropriately) named DataObject
.
IDataObject ido = new DataObject();
With the IDataObject
interface in hand we can begin to use it. The
purpose of the IDataObject
is to "[Provide] a format-independent mechanism for
transferring data" (MSDN, IDataObject
interface). Those familiar with COM
will know this object well. It can store one object of each format that you
pass in. Thus if you need to store 3 Text formatted objects, you'll need
to wrap them into a custom format which I'll explain later.
ido.SetData(DataFormats.Text, true, myString);
That line of code adds a new Text formatted object with the value of myString
to the DataObject
, and allows it to be converted to other types as well.
This will be a key point that you find in Win32 using the Clipboard
. Data
is often in multiple formats to allow it to be used in a multitude of programs.
Typically data will be stored in a custom format, as text, and possibly a bitmap
if it represents something graphical. This allows for pasting into many
applications and into itself in a native format.
Adding multiple, additional, formats is as easy as additional calls to SetData
with differing formats.
Clipboard.SetDataObject(ido, true);
This line finally places the data on the clipboard where it can be read by
any program. The true
argument tells the Clipboard
to make the data
available even after the program quits. Later I'll introduce another
important point about the second parameter, which isn't mentioned by MSDN.
With the data copied to the Clipboard
you can now paste it as you did before;
check for the proper data format then retrieve it if it exists.
IDataObject ido = Clipboard.GetDataObject();
if(ido.GetDataPresent(DataFormats.Text))
{
textBox1.Text = (string) ido.GetData(DataFormats.Text);
}
Custom data formats
A custom data format can be anything; whether a special text formatting such
as HTML or XML or it can be an instance of a class. To use a custom
format, first you register it with Windows. In the demo application you
will find that I register the type with Windows in the static constructor for
the custom type class, CustomData.
static CustomData
{
format = DataFormats.GetFormat(
typeof(CustomData).FullName
);
}
The code typeof(CustomData).FullName
merely retreives the full name of the
type; since I needed to choose a unique name I figured that would do.
GetFormat
returns an instance of the DataFormats.Format
class which I store for
use as a static property to access the Name
and ID
of the format. The name doesn't
have to be relevant just unique, Windows registers a Format17 which it uses for screenshots.
The only unique part about creating a custom format that uses a class is that
the class must be serializable, that is it needs to have the Serializable
attribute applied to it.
[Serializable()]
public class CustomData
.....
To use it on the clipboard you do so as you would any other format.
IDataObject ido = new DataObject();
CustomData cd = new CustomData(myImage, myString);
ido.SetData(CustomData.Format.Name, false, cd);
Retrieving data from it is done the same as before.
IDataObject ido = Clipboard.GetDataObject();
if( ido.GetDataPresent(CustomData.Format.Name) )
{
CustomData cd = (CustomData)
ido.GetData(CustomData.Format.Name);
}
Persisting data on the clipboard, ie the 2nd parameter to SetDataObject
According to MSDN the second parameter tells the Clipboard
object whether the
data inside should be persisted when the application exits. BUT that isn't
the full story. What it actually does is delay the serialization of the
data until when the data is actually needed.
To see this in action run the included ClipboardTest2 application, uncheck
"Persist Data"; then proceed to Draw, Copy, and Paste, then Draw and Paste.
You'll see that even though the data was copied only once the new data is shown.
The reason for this lies in that I continue to operate on the same object
that was passed into the IDataObject
's SetData
method, since we told
the Clipboard object NOT to serialize the data until needed, that is what it did. On top of that it
will reserialize the data everytime the data is requested. Oddly though it
only updates the format specified, none of the auto-converted formats are touched once
the initial serialization of the format occurs.
To demonstrate this open up the ClipboardTest2 application, again
uncheck "Persist Data"; and then Draw, Copy, and Paste. Proceed to Draw and
Paste, then open up your favorite image editing software; MSpaint will
do. Now Paste the bitmap there; do some more Draw and Pastes then Paste
again in another copy of MSPaint. You'll see the same bitmap was pasted
in both MSPaint's.
This seems like a bug, but really it is a bug in my use. Once you place
an object on the clipboard the intended result is that object stays the same;
not to change it like I did. This means the optimal solution is to create
a copy and cache it somewhere until it is needed by the Clipboard object.
If you choose to not persist data to the clipboard, upon exiting the
application you should re-place the data on the clipboard and persist it; so
that the contents are still there for the user to use.
Conclusion
There you have it folks, the Clipboard
in all its glory. Hope I didn't
bore you too much. As always leave questions or comments below; and please
e-mail me any bugs you find.
For those interested in the Visual Style I used in the screen shots, I got it
from themexp.org;
you'll also need StyleXP or the free update
to uxtheme.dll from the same site.