

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.
| You must Sign In to use this message board. |
|
|
 |
|
 |
Hello,
First of all thanks for an interesting article.
I have one question but I'm not sure whether it's directly related to this article or not. I am trying to accomplish the same thing as this article does: How to drag a virtual file from your app into Windows Explorer but in c#. From one of the comments to that article I found that "COleDataSource is a wrapper for IDataObject interface." and this article might help me. I am not quite sure what to do next as I don't know win32 well. Any thoughts?
Thanks
Sorry if my question is inappropriate.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Example:
public interface IBase{}
public class Derived : IBase{}
If you push onto the Clipboard as Derived, and try to GetData(typeof(IBase).FullName), it won't work (makes sense when you think about it). The Clipboard doesn't support polymorphism in this manner.
My solution was to make everything DataFormats.Serializable, and then check if deserializedObject is IBase (as an example).
If you have a better solution I'd like to hear it.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
i have an application which should get the data from clipboard, every second the data changes so i used threading but i need the act clipboard obj in every new called thread - every time the first ref - but i dont know how solve this - no idea!?
System.Threading.Timer timer;
public void createDataTimer(int period) { TimerCallback tc = new TimerCallback(NewData);
ClsTimer clsTimer = new ClsTimer(); IDataObject ClipboardDataO = Clipboard.GetDataObject(); clsTimer._clipboard = ClipboardDataO;
timer = new System.Threading.Timer(tc, clsTimer, 0, period); }
public void NewData(Object obj) { Console.WriteLine("\r{0}", DateTime.Now);
if (obj is ClsTimer) { ClsTimer clsTimer = (ClsTimer)obj; textBox1.Text = clsTimer.GetClipboardString(); } }
class ClsTimer { public IDataObject _clipboard; public IDataObject clipboard { get { return _clipboard; } set { _clipboard = value; } }
public string GetClipboardString() { if (_clipboard.GetDataPresent(DataFormats.Text)) { return _clipboard.GetData(DataFormats.Text).ToString(); } else { return ""; } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I want to store list of XmlElements in Clipboard. How to serialize XmlElement in to IDataObject ? Can anybody help ?
Thanks Amey
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi,
I'm having problems with Clipboard.GetDataObject() sometimes working and sometimes raise an error "Object reference not set to an instance of an object". In my application I'm trying to get the contents of a word document. Using the debugger I simply can't find what is the problem as everything is performed exactly the same when it is working and when it is not. I've tried to clear the clipboard before copy with no luck. I've scanned the internet forums and didn't find any answer though other people seem to have the same issue. I'm using .NET 2.0. Below please find the code: //Clipboard.Clear(); doc.ActiveWindow.Selection.WholeStory(); doc.ActiveWindow.Selection.Copy();
IDataObject data = Clipboard.GetDataObject(); <= Problem here string text = data.GetData(DataFormats.Text).ToString();
I thank you for your help.
|
| Sign In·View Thread·PermaLink | 1.20/5 |
|
|
|
 |
|
|
 |
|
 |
I have recently had the same error happen on a page. What worked for me was adding the following piece of code to the .aspx page declaration:
AspCompact="true"
making the whole declaration look something like the following: <![CDATA[<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Test_Clipboard_App._Default" AspCompat="true" %>]]>
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I am trying to place an object of my own class into clipboard by clicking on the object. Here inside my rectagle class i am passing this as object to the clipboard ,but it is not getting added to clipboard. Is it because of i am placing object into clipboard inside the class ? Please give a solution for this. Advance thanks. Regards K Anvar sadath
Anvar
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello,
First of all, congratulations for this very good article.
I try to use this idea for my implementation, i'm trying to create a custom format: a simple ArrayList. But this does not work.
I do the SetData like you, but when I try to do the paste, the IDataObject is always null... I don't see why:(
Any Ideas?
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
 |
Hey, James T. Johnson your eye looks like it needs to be checked out. It may have cancer. The white spot and not red reflection in pictures usually is a good sign of cancer.
If none of your new pictures have it then do not worry.
Hope you are ok
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Thanks for the concern 
I've been to a few different optometrists since that picture was taken and none have seen any problems with my eyes aside from near-sightedness.
James
|
| Sign In·View Thread·PermaLink | 3.00/5 |
|
|
|
 |
|
 |
You do not get it! If you have more pictures (same relevant time) like this one then you need to find someone that knows about that rare disease. I seen it on the Monteal Jordan show and it’s not easily spotted, except in pictures. If you have more pictures with the white dot and not the red, you need to be concerned.
Rare disease
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Hey Can you help figure out how to Uploaded Word Doc and write the text in ASP.NET2 C#.
I think there is something wrong with the Copy and Clipboard because I get an error from the IDataObject getData when I try to GetData to write the text its not set to an instance of an object.
Object filename = filResume.PostedFile.FileName; ApplicationClass word = new ApplicationClass(); Object confirmConversions = Type.Missing; Object readOnly = Type.Missing; Object addToRecentFiles = Type.Missing; Object passwordDocument = Type.Missing; Object passwordTemplate = Type.Missing; Object revert = Type.Missing; Object writePasswordDocument = Type.Missing; Object writePasswordTemplate = Type.Missing; Object format = Type.Missing; Object encoding = Type.Missing; Object visible = Type.Missing; Object openAndRepair = Type.Missing; Object documentDirection = Type.Missing; Object noEncodingDialog = Type.Missing; Object XmlTrans = Type.Missing;
Document doc = word.Documents.Open(ref filename, ref confirmConversions, ref readOnly, ref addToRecentFiles, ref passwordDocument, ref passwordTemplate, ref revert, ref writePasswordDocument, ref writePasswordTemplate, ref format, ref encoding, ref visible, ref openAndRepair, ref documentDirection, ref noEncodingDialog, ref XmlTrans); try { // Copy Read Text doc.ActiveWindow.Selection.WholeStory(); doc.ActiveWindow.Selection.Copy(); IDataObject data = Clipboard.GetDataObject();
//Here is The Error Line: Object reference not set to an instance of an object.
Response.Write(data.GetData(DataFormats.Text).ToString() + ""); //Empty Clipboard Clipboard.Clear();
} catch (Exception ex) { Response.Write(ex + ""); }
<!--Add to Web.config Assembly -->
using Microsoft.Office.Interop.Word; using System.Windows.Forms;
<asp:FileUpload ID="filResume" style="font: 11;width:100%" runat="server" onchange="Val(this.id, 'filResume', 'Resume', 'hdID')" />
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hey Can you help figure out how to Uploaded Word Doc and write the text in ASP.NET2 C#.
I think there is something wrong with the Copy and Clipboard because I get an error from the IDataObject getData when I try to GetData to write the text its not set to an instance of an object.
Object filename = filResume.PostedFile.FileName; ApplicationClass word = new ApplicationClass(); Object confirmConversions = Type.Missing; Object readOnly = Type.Missing; Object addToRecentFiles = Type.Missing; Object passwordDocument = Type.Missing; Object passwordTemplate = Type.Missing; Object revert = Type.Missing; Object writePasswordDocument = Type.Missing; Object writePasswordTemplate = Type.Missing; Object format = Type.Missing; Object encoding = Type.Missing; Object visible = Type.Missing; Object openAndRepair = Type.Missing; Object documentDirection = Type.Missing; Object noEncodingDialog = Type.Missing; Object XmlTrans = Type.Missing;
Document doc = word.Documents.Open(ref filename, ref confirmConversions, ref readOnly, ref addToRecentFiles, ref passwordDocument, ref passwordTemplate, ref revert, ref writePasswordDocument, ref writePasswordTemplate, ref format, ref encoding, ref visible, ref openAndRepair, ref documentDirection, ref noEncodingDialog, ref XmlTrans); try { doc.ActiveWindow.Selection.WholeStory(); doc.ActiveWindow.Selection.Copy(); IDataObject data = Clipboard.GetDataObject();
Response.Write(data.GetData(DataFormats.Text).ToString() + ""); Clipboard.Clear();
} catch (Exception ex) { Response.Write(ex + ""); }
<!--Add to Web.config Assembly --> "Microsoft.Office.Interop.Word, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"/>
using Microsoft.Office.Interop.Word; using System.Windows.Forms;
<asp:FileUpload ID="filResume" style="font: 11;width:100%" runat="server" onchange="Val(this.id, 'filResume', 'Resume', 'hdID')" />
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hey Can you help figure out how to Uploaded Word Doc and write the text in ASP.NET2 C#.
I think there is something wrong with the Copy and Clipboard because I get an error from the IDataObject getData when I try to GetData to write the text its not set to an instance of an object.
Object filename = filResume.PostedFile.FileName; ApplicationClass word = new ApplicationClass(); Object confirmConversions = Type.Missing; Object readOnly = Type.Missing; Object addToRecentFiles = Type.Missing; Object passwordDocument = Type.Missing; Object passwordTemplate = Type.Missing; Object revert = Type.Missing; Object writePasswordDocument = Type.Missing; Object writePasswordTemplate = Type.Missing; Object format = Type.Missing; Object encoding = Type.Missing; Object visible = Type.Missing; Object openAndRepair = Type.Missing; Object documentDirection = Type.Missing; Object noEncodingDialog = Type.Missing; Object XmlTrans = Type.Missing;
Document doc = word.Documents.Open(ref filename, ref confirmConversions, ref readOnly, ref addToRecentFiles, ref passwordDocument, ref passwordTemplate, ref revert, ref writePasswordDocument, ref writePasswordTemplate, ref format, ref encoding, ref visible, ref openAndRepair, ref documentDirection, ref noEncodingDialog, ref XmlTrans); try { // Copy Read Text doc.ActiveWindow.Selection.WholeStory(); doc.ActiveWindow.Selection.Copy(); IDataObject data = Clipboard.GetDataObject();
//Here is The Error Line: Object reference not set to an instance of an object.
Response.Write(data.GetData(DataFormats.Text).ToString() + ""); //Empty Clipboard Clipboard.Clear();
} catch (Exception ex) { Response.Write(ex + ""); }
<!--Add to Web.config Assembly --> <!-- <add assembly="Microsoft.Office.Interop.Word, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
using Microsoft.Office.Interop.Word; using System.Windows.Forms;
<asp:FileUpload ID="filResume" style="font: 11;width:100%" runat="server" önchange="Val(this.id, 'filResume', 'Resume', 'hdID')" -->
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
There is nothing wrong with the Open, Copy, Clipboard, but the "data.GetData(DataFormats.Text).ToString();"
It is not filling up with Word.doc data like it would in a Windows App.
This Web App Opens my MS Word, Selects, Copies, and applies to ClipBoard, yet I am unable to retrieve the data. Remember I am using C# for ASP.NETv2. The code is to copy the uploaded resume and store as a CLOB in Oracle along with the file as a BLOB.

Object filename = filResume.PostedFile.FileName; ApplicationClass word = new ApplicationClass();
Object confirmConversions = Type.Missing; Object readOnly = Type.Missing; Object addToRecentFiles = Type.Missing; Object passwordDocument = Type.Missing; Object passwordTemplate = Type.Missing; Object revert = Type.Missing; Object writePasswordDocument = Type.Missing; Object writePasswordTemplate = Type.Missing; Object format = Type.Missing; Object encoding = Type.Missing; Object visible = Type.Missing; Object openAndRepair = Type.Missing; Object documentDirection = Type.Missing; Object noEncodingDialog = Type.Missing; Object XmlTrans = Type.Missing;
Document doc = word.Documents.Open(ref filename, ref confirmConversions, ref readOnly, ref addToRecentFiles, ref passwordDocument, ref passwordTemplate, ref revert, ref writePasswordDocument, ref writePasswordTemplate, ref format, ref encoding, ref visible, ref openAndRepair, ref documentDirection, ref noEncodingDialog, ref XmlTrans);
try { // Copy Read Text doc.ActiveWindow.Selection.WholeStory(); doc.ActiveWindow.Selection.Copy(); //Clipboard.Clear(); Response.Write(Clipboard.GetData(DataFormats.Text).ToString()); IDataObject data = Clipboard.GetDataObject(); Response.Write(data.GetData(DataFormats.Text, true).ToString()); //Response.Write(Clipboard.GetDataObject().GetData(DataFormats.Text).ToString()); //Response.Write(Clipboard.GetDataObject().GetData(DataFormats.Text).ToString()); } catch (Exception ex) { Response.Write(ex + "");
} finally {
// Close the active document without saving changes. Object saveChanges = WdSaveOptions.wdDoNotSaveChanges; Object originalFormat = Type.Missing; Object routeDocument = Type.Missing; word.Documents.Close(ref saveChanges, ref originalFormat, ref routeDocument); word.Quit(ref saveChanges, ref originalFormat, ref routeDocument); }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This page is really helpful, thanks!
I am trying to display multiple items on the clipboard. For instance, in Microsoft PowerPoint, there is a clipboard task pane that shows all the clipboard items.
When two text items are added to the clipboard, the MS PowerPoint clipboard only shows the last one.
DataObject dao = new DataObject(); dao.SetData(DataFormats.Text, "text1"); dao.SetData(DataFormats.Text, "text2"); Clipboard.SetDataObject(dao, true); In this case, only "text2" is added to the clipboard. When I use your custom data format, both items are added. However, they are not visible on the clipboard.
Do you have any experience with showing custom formats on the clipboard?
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
The artical was helpful but I am still having difficulty adding a custom format. When I put multiple formats on the clipboard I can retrieve the TEXT data. This tells me that I am following the correct procedure to place things in the dataobject. When I query for Formats my custom format is registered. When I query about the data GetDataPresent() it says the data is available. When I actually try and retrieve the data, GetData(), I get a null for a response. This makes me believe that there is something about the object that is stopping the serialization.
What I am trying to add to the clipboard is a DataRow. The DataSet that is generated by VS .NET has the DataSet declared:
[Serializable()] public class myDataSet : DataSet { tables{} rows{} }
This would leave me to believe that the dataset and it's components are serializable. Do I actually have to add the [Serializable] attribute to the rows I want to serialize?
(Yes, there is a simple work around, I simply store the unique id and on Paste, create the copy of the datarow. I would just like to figure out what isn't working.)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Is to copy the DataRows ItemArray instead of the row itself:
DataObject dataObject = new DataObject(); dataObject.SetDatathis.clipboardFormat.Name,false,row.ItemArray); Clipboard.SetDataObject(dataObject);
When I retrieve the Row:
Object[] itemArray = (Object[])d.GetData(this.clipboardFormat.Name ); DataRow row = dataView.Table.NewRow(); row.ItemArray = itemArray;
//Then I go and add (CopyX) to the first PrimaryKey column so I have unique keys..
I just hope that when the light in my head finally goes on, that my brain is still in there! 
|
| Sign In·View Thread·PermaLink | 4.11/5 |
|
|
|
 |
|
 |
This article helped me - my problem was that I hadn't marked my class as Serializable (as well as missing the comment in the .NET documentation for the DataObject class that they recommend you use that class rather than implementing IDataObject for yourself)
Thanks
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi, I try to copy a Metafile object from a .Net application to another not .Net applications. It don't work. The not .Net application recognize the format beacause the Paste feature is enabled, but when i want to paste the application displays an error. What's the issue ? Many thanks. Benoit
FONTAINE Benoit
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Sorry I haven't replied until now. I haven't forgotten about you; I've been too busy to take a look into why it didn't work.
What error does it display? Does it do that for all metafiles or just certain ones?
James
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
See http://support.microsoft.com/default.aspx?scid=kb;en-us;323530 PRB: Metafiles on Clipboard Are Not Visible to All Applications
Wout
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Cool !  Mauricio Ritter | 4:46 4 Jun '02 |
|
 |
Congrats James... cool article. Are those "types" stored in the clipboard the same as the mime type (the ones at windows registry) ? I didn´t download the source yet (I don´t have VS.NET at work).
Mauricio Ritter - Brazil Sonorking now: 100.13560 Trank
The alcohol is one of the greatest enemys of man, but a man who flee from his enemys is a coward. 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|