Clipboard Ring Application

Introduction

The Windows Clipboard is used by many applications, such as word formatters and word processors, as a temporary repository for data. The most obvious examples are Cut, Copy, and Paste. the Clipboard 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 whether to process it.

In .NET, the functionalies for playing with the Windows system Clipboard has been clubbed into the Clipboard class. To add data to Clipboard, you can use the SetDataObject method of the Clipboard class. 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, even though there are specific data objects for specific formats.

Background

This application was basically developed for my personal use. Many times, I would cut a block of code to paste somewhere else and, in the meantime, if I had to look into something else (like attending to someone), by the time I came back I might have forgotton that I was holding something important on the Clipboard and I used 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, later in the day.

Using the Code

Retrieving text data from Clipboard

The Clipboard class has a method, GetDataObject(), that retrieves the data currently in the system Clipboard. In fact, GetDataObject() returns an object that implements the IDataObject interface, which has three dominant methods: GetDataPresent, GetData, and GetFormats.


  • GetDataPresent: Determines whether the current data on theClipboard can be converted to the format you specify.

  • GetData: Retrieves the data associated with the format you specify from Windows Clipboard.

Note: The DataFormats class has predefined Clipboard data format names. These can be provided to the above two methods as parameters to verify the data format.

if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Text))
{
   string s = Clipboard.GetDataObject().GetData(DataFormats.Text).
              ToString();
}

In the preceding piece of code, the GetData method is used to retrieve data from the system Clipboard. The format of the data to be retrieved is provided as a parameter. Because the system Clipboard is shared by multiple applications, before retrieving data, you must ensure that the format of current Clipboard data is what your appliaction expects. So, you make a check for the data format of the data to be retrieved with a call to the GetDataPresent method; this method returns a boolean value that determines whether the current data on the Clipboard can be converted to the data format you specify. To specify the data format, it is advisable to use the DataFormats class that contains static fields representing system Clipboard formats. DataFormats.Text is provided for string data. An alternative to using the DataFormats class is to provide the name of the format as a string.

Setting text data to the Clipboard

The Clipboard class has a method, SetDataObject(). This method places the specified data on the Clipboard. This method has three interesting overloaded clones:


  1. SetDataObject(object data): Places data on the system Clipboard.
  2. Note: The data placed on the Clipboard by this method is non-persistent. In other words, the data is not retained on Clipboard once the application exits.

  3. 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.

  4. 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 piece of code above, the SetDataObject method sets the string “C# is the best” on the system Clipboard. The text remains on the Clipboard until it is replaced by any application. And, the string remains there even after your application exits because the second parameter of the method is set to true, which indicates the data will be persistant.

Retrieving image from the 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);
}

The GetDataPresent method verifies whether an image (bitmap) object is present on Clipboard; the 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 that can be directly assigned to Image objects or controls.

Setting an Image to the Clipboard

This is also similar to setting text to the Clipboard.

Clipboard.SetDataObject((Bitmap)lstBoxCpyItms.SelectedItem, true);

The overloaded versions of SetDataObject were explained earlier in this article.

Until now, the discussion has been about the basic usage of the Clipboard class. Now, you will get into the ClipboardRing application.

Clipboard Ring Application

This is the method that retrieves text/image from the system Clipboard and makes an entry into a ListBox, where it resides until the application exits. The current data on the Clipboard is retrieved and added to the ListBox Items collection. By default, only objects with the Text format are retrieved from the Clipboard and added to the ListBox. To store images, right-click the notify icon on the 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 strClipCurrent variable. The next time, it is compared with the current string on the 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 the code make things very clear. The image comparison logic has been taken from markrouse’s article. Thanks to markrouse. If I hadn’t found this solution, I would have discarded the image storing functionality from the application because all other options I reached to compare images were really crude and not presentable.

The basic logic behind the image comparison is that the hash of two 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.
                  if (CompareBitmaps(bmp, (Bitmap)obj))
                  {
                     addImg = false;
                  }
               }
            }
            // Image is different, so add it to the list.
            if (addImg)
            {
               //imageList1.Images.Add(bmp);
               lstBoxCpyItms.Items.Add(bmp);
               bmpClipCurrent = bmp;
            }
            // First image retrieved from Clipboard.
            // Therefore, add to list.
         }
         else
         {
            //imageList1.Images.Add(bmp);
            lstBoxCpyItms.Items.Add(bmp);
            bmpClipCurrent = bmp;
         }
      }
   }
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read