Introduction
Clipboard is used by many applications like word formatters and word processors as a temporary repository for data. The most obvious example is Cut, Copy and Paste. It is also useful for transferring data from one application to another, because the Clipboard is common across applications(processes). When data is added to the Clipboard, the data format can be specified so that other applications can recognize the format and decide to process it or not.
In .Net the functionalies for playing with Windows system clipboard has been clubbed into the class Clipboard. To add data to Clipboard SetDataObject method of Clipboard class can be used. You can pass any object to this method, but to add data in multiple formats, you must first add the data to a DataObject which is generic, eventhough there are specific data objects for specific formats.
Background
This application was basically developed for my personal use. It happened many times that I cut a block of code to paste somewhere else and in the meanwhile if I have to look into something else (like attending to someone etc.), by the time I come back I might have forgotton that I was holding something important on clipboard and I use the clipboard space for something else. As a remedial measure, I decided to develop an application that can hold all the entries that make a visit to my Clipboard, so that I can retrieve it back even at some point of time, down the day.
Using the code
Retrieving text data from clipboard
Clipboard class has a method, GetDataObject() which retrieves the data that is currently in the system clipboard. Infact GetDataObject() returns an object that implements the IDataObject interface, which has 3 dominant methods(GetDataPresent, GetData and GetFormats).
- GetDataPresent - Determines if the current data in Clipboard can be converted to the format we specify.
- GetData - Retrieves the data associated with the format we specify from Windows Clipboard.
Note: DataFormats class has predefined Clipboard data format names, which can be provided to the above 2 methods as parameter to verify the data format.
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Text))
{
string s = Clipboard.GetDataObject().GetData(DataFormats.Text).ToString();
}
In the above piece of code, GetData method is used to retrieve data from the system Clipboard. The format of the data to be retrieved is provided as parameter. As the system Clipboard is shared by multiple applications, before retrieving data, we must ensure that the format of current clipboard data is the same that our appliaction expects. So we make a check for the data format of the data to be retrieved, with a call to GetDataPresent method. GetDataPresent method returns a boolean value which determines whether the current data on the Clipboard can be converted to the data format we specify. To specify the data format, it is advicable to use the DataFormats class which contains static fields representing system Clipboard formats. DataFormats.Text is provided for string data. Alternative to using the DataFormats class is to provide the name of the format as string.
Setting text data to Clipboard
Clipboard class has a method, SetDataObject(). This method places the specified data on the Clipboard. This method has 3 interesting overloaded clones.
- SetDataObject(object data) - Places data on the system Clipboard.
Note: The data placed on Clipboard by this method is non-persistent, ie., the data is not retained on Clipboard once the application exits. - SetDataObject(object data, bool copy) - Places data on the system Clipboard and specifies whether the data should remain on the Clipboard after the application exits.
- SetDataObject(object data, bool copy, int RetryTimes, int RetryDelay) - Attempts to place data on the system Clipboard the specified number of times and with the specified delay between attempts.
Clipboard.SetDataObject("C# is the best", true);
In the above piece of code, SetDataObject method sets string "C# is the best" on the System Clipboard. The text remains on the clipboard till it is replaced by any application. And the string remains there even after our application exits, because the second parameter of the method is set to true, which indicates the data to be persistant.
Retrieving image from Clipboard
This is exacly the same as retrieving text data, except that the data format given is DataFormats.Bitmap instead of DataFormats.Text
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Bitmap))
{
Bitmap bmp = (Bitmap)Clipboard.GetData(DataFormats.Bitmap);
}
GetDataPresent method verifies if an image(bitmap) object is present on clipboard and GetData method retrieves the image as a bitmap object.
An alternative for this in .Net 2.0 is
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Bitmap))
{
Bitmap bmp = (Bitmap)Clipboard.GetImage();
}
The GetImage method returns an Image object which can be directly assigned to Image objects or controls.
Setting Image to Clipboard
This is also similar to setting text to clipboard.
Clipboard.SetDataObject((Bitmap)lstBoxCpyItms.SelectedItem, true);
The overloaded versions of SetDataObject has been explained earlier, in this article.
Till now our discussion has been about the basic usage of the Clipboard class.
Now we shall get into the ClipboardRing application.
Clipboard Ring Application
This is the method which retrieves text/image from the system Clipboard and makes an entry into a ListBox, where it resides till the application exits. The current data on the clipboard is retrieved and added to the ListBox Items collection. By default only objects with Text format is retrieved from Clipboard and added to the ListBox. To store images, right click on the notify icon on system tray and select the "Process Image" option. Then the boolean variable processImage is set to true.
Duplicate entries are restricted to the listbox. When a new string entry is made to the listbox items collection, it is stored in a variable strClipCurrent. Next time, it is compared with the current string on Clipboard and only if it is different, it is added to the listbox. Also, if the string is already an entry in the listbox it is not added again. In the case of images also this checking is made, but the logic applied is a bit different, but very similar. I believe the comments in code make things very clear. The image comparison logic has been taken from markrouse's article. Thanks to markrouse. If I couldn't find this solution I would have discarded the image storing functionality from the application, because all other options I reached, for comparing images were really crude and not presentable.
The basic logic behind the image comparison is that the hash of 2 identical objects are equal and different for unidentical objects. The comparison is done by passing an array of bytes and comparing their hash values. This is a really fast method.
private void AddToBoard()
{
bool addImg = true;
// Before retrieving Text from the Clipboard make sure the
// current data on Clipboard is for type text.
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Text))
{
string s = Clipboard.GetDataObject().GetData(DataFormats.Text).ToString();
// strClipCurrent has the last string retrieved from the Clipboard.
// This checking is to avoid the unnecessary looping of the ListBox control
// unless a new string has come to the clipboard.
if (s != strClipCurrent)
{
// This checking is to avoid multiple entries in ListBox control.
if (!lstBoxCpyItms.Items.Contains(s))
{
lstBoxCpyItms.Items.Add(s);
strClipCurrent = s;
}
}
}
// This option is enabled only when user explicitly enables it
// This option is to manage images in the Clipboard.
if (processImage)
{
if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Bitmap))
{
Bitmap bmp = (Bitmap)Clipboard.GetImage();
// if bmpClipCurrent is not null means this is not the first image
// retrieved from the clipboard. Therefore compare with the
// previous image if they are same else add to list.
if (bmpClipCurrent != null)
{
foreach (object obj in lstBoxCpyItms.Items)
{
if (obj.GetType().ToString() == "System.Drawing.Bitmap")
{
// Comparing if the 2 bitmaps are the same.
// Returns true is identical else false.
Joshua OsmandPosted Aug 16, 2007, 3:39 PM
I'm not sure if my comment went through so pardon me if you get this twice. Is there a way to change the default dpi in the clipboard from 96 to something else? This would be quite handy for images which don't print well at 96 dpi. Thank you. David Washington, DC