Persisting Clipboard History Across Sessions with our DXCore Visual Studio Plug-in

Welcome to Part 7 of the series showing how to build plug-ins for Visual Studio using DXCore.

So far we've:

In today's relatively short post (don't worry - it'll be intense tomorrow), we'll show how to persist the clipboard history across Visual Studio sessions.

Persisting History Across Sessions

One of the things you've no doubt noticed is that when we start a new session and immediately bring up the Clipboard History, it is completely empty. Some developers might find it handy to have access to entries from the last session of Visual Studio. We'll add this as an optional behavior, and show how to use the DXCore's DecoupledStorage class to write and read data to and from the user's settings location.

About DecoupledStorage

The DecoupledStorage class embodies methods that make it easier to read and write settings. Storage locations can be specified with category and page name strings, or with a type reference to an options page. You can create a new DecoupledStorage instance by calling CodeRush.Options.GetStorage, or if you have a type reference to an options page, you can get the associated storage for that options page directly by calling its static GetStorage method.

Whenever I work with persisting settings I like to start with the code that saves. Inside ClipboardHistory.cs, let's create a new method that persists the important settings:

public static void Save()
  using (DecoupledStorage settings = CodeRush.Options.GetStorage("Editor\\Clipboard", "History"))
    settings.WriteInt32("Summary", "LastIndex", LastIndex);
    settings.WriteInt32("Summary", "Cursor", FrmClipHistory.CursorIndex);
    for (int index = 0; index <= LastIndex; index++)
      string indexStr = index.ToString();
      ClipboardHistoryEntry entry = Entries[index];
      settings.WriteString("Entries", "Text" + indexStr, entry.Text, true);  // string is encoded
      settings.WriteString("Entries", "Language" + indexStr, entry.Language);

So the code above effectively saves the LastIndex (so we know how many entries are stored), and the Cursor position (so we can restore that as well in the next session), and all the entries. When the DecoupledStorage instance is disposed (at the end of the using block above), all data is flushed out to disk. For performance reasons, calls to WriteXxxx generally do not write to disk until the DecoupledStorage instance is disposed.

IMPORTANT: There are several ways to store strings, including a dedicated WriteStrings method which is useful if you have an array of strings. The overload we're using to save the Text in the code above has an additional parameter, encoded, which allows us to specify true for HTML encoding. This option is useful for saving strings containing new line characters or other special characters.

Next, let's add a Load method that reads the persistent settings and creates a new ClipboardHistoryEntry for each entry persisted:

public static void Load()
  using (DecoupledStorage settings = CodeRush.Options.GetStorage("Editor\\Clipboard", "History"))
    int lastIndex = settings.ReadInt32("Summary", "LastIndex", -1);
    FrmClipHistory.CursorIndex = settings.ReadInt32("Summary", "Cursor", 0);
    if (lastIndex > LastIndex)
// Don't read more entries than we can support.
lastIndex = LastIndex;
    for (int index = 0; index <= lastIndex; index++)
      string indexStr = index.ToString();
      string text = settings.ReadString("Entries", "Text" + indexStr, String.Empty, true);  // string is encoded
      string languageID = settings.ReadString("Entries", "Language" + indexStr, String.Empty);
      Entries[index] = new ClipboardHistoryEntry { Text = text, Language = languageID };

In the code above note that we're setting the static property FrmClipHistory.CursorIndex, but that property is currently read-only. Let's change that and add a setter to the CursorIndex property, inside FrmClipHistory.cs

internal static int CursorIndex
GetIndex(_CursorRow, _CursorColumn);


That should properly restore the cursor position.

Finally, inside PlugIn1.cs, let's add calls to our new Load and Save methods from our plug-in's InitializePlugIn and FinalizePlugIn methods, like this:

public override void InitializePlugIn()

and this:

public override void FinalizePlugIn()


Let's test it.

Testing Persistent History

Alright, you know the drill by now.


Click Run, and in the second instance of Visual Studio, open a source file and perform several copy operations to populate the clipboard history.

Press Ctrl+Shift+Insert to bring up the Clipboard History form. Move the cursor to a distinct position.

Press Escape to close the Clipboard History and then exit Visual Studio.

Click Run one more time and in the second instance of Visual Studio press Ctrl+Shift+Insert to bring up the clipboard history. This time you should see all the entries from the previous session, and the cursor should still be selecting the previously selected entry. Excellent.

Tomorrow we'll add a professional-quality options page to the DevExpress Options dialog so developers can customize clipboard history behavior (including changing the dimensions of the grid).

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.