Introduction: MultiSlot Clipboard

The Visual Studio IDE is one of the most advanced development environments that allow the development any type of software. However, even this complex and multipurpose tools still can receive some improvements.

This Instructable focus on a specific feature that Visual Studio (and many other Windows tools) lack: more than one clipboard slot to store data. Although the traditional clipboard is very useful, in particular to those who edit code in the text format, almost every tool for the Windows, Linux or Mac OS platform only provide one slot to cut/copy and pasta data.

The advantages of having more than one clipboard slot are twofold: allow the user to retain more data in memory for later use and avoid the non-intentional replacement of what is already on the only clipboard slot. Despite the fact that developers are accustomed to work with a single copy/past container, there is no technical reason to provide only one area that store temporary data that can be used when working with code.

Based on this context, this Instructable explain how to create the MultiSlotClipboard, an extension to Visual Studio 2013 that allows five extra clipboard slots to copy/cut and paste data on the code. Each new slot contains a circular history of the last ten data items stored.

The code for this project can be found here: https://github.com/pichiliani/MultiSlotClipboard

Step 1: What You Will Need

To create the MultiSlotClipboard extension you will need two simple softwares:

1) Visual Studio 2013 community edition. This edition is free and can be obtained on the following address: https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx

2) The Visual Studio 2013 SDK, which is also free and available at this address: https://www.microsoft.com/en-us/download/details.aspx?id=40758

First install Visual Studio 2013 and make sure everything is working. You do not need any extra packages and a simple installation with only the C# language will be enough.

Next install the Visual Studio SDK, which is a set of libraries needed to develop and extension for Visual Studio.

Both installations are straightforward and require an internet connection.

Step 2: Setting Up the Environment

The second step is to setup the environment by creating a new project.

We are going to use one of the templates provided by the Visual Studio 2013 SDK. First, open Visual Studio and click the File menu, chose the New option and then the Project…, as the Figure bellow shows.

You will see the New project window. On the left side chose the Extensibility option under the Templates, Visual C#
hierarchy. On the right side panel of the screen choose the Visual Studio Package. Don’t forget to name the project and the solution MultiSlotClipboard and store it on a convenient folder using the text boxes on the bottom part of the window. Click on the OK button to create the solution and the project, as the Figure bellow shows.

After the New Project Window a wizard will ask some questions about the project. The first window just present general information and you can click the Next button.

The next window of the Wizard asks for the language on which the package will be created and if you want to generate a new key file. Choose the options “Visual C#” and “Generate the new key file to sign the assembly” and click Next button, as the Figure bellow shows.

The Next Window asks for metada information about the project. Fill in the field VSPackage name with the MultiSlotClipboard value and feel free to add any extra data to identify the package, as the next Figure shows.

We are almost done with the Wizard. The next Window asks what type of package you want to create. For this project we are going to create a new Tool Window that will contain controls to cut, copy, and paste the data for the extra five slots. Therefore, make sure the “Tool Window” checkbox is market.

Since our project has a Window we have to named it and provide an identifier for the Command ID, which is the name of an internal variable. You can use the default value for the Command ID and insert the MultiSlotClipboard value for the Window name field, as the shown on the next Figure.

The last window of the Wizard asks for the generation of tests. Since this is a simple project we will not create any test. Therefore we must uncheck the two check boxes of this last Window.

After some processing Visual Studio will create all the necessary files for the project, including some code, configuration, and design files that are going to be changed on the next steps of this Instructable.

The basic structure of the project is provided by the MultiSlotlipboardPackage.cs, which is a class that inherits from the Package class. The project also has a control, named MyControl, which has the MyControl.xaml and MyControl.xaml.cs files. These are the three files we are going to change to implement our solution.

Before we change anything on the project we need to add some references. Although the template and the Wizard added the references for some files, there are additional references that need to inserted on the project manually. The files needed are usually stored in the folder \Microsoft Visual Studio 12.0\VSSDK\VisualStudioIntegration\Common\Assemblies\v4.0\

· Microsoft.VisualStudio.CoreUtility (File: Microsoft.VisualStudio.CoreUtility.dll)

· Microsoft.VisualStudio.Editor (File: Microsoft.VisualStudio.Editor.dll)

· Microsoft.VisualStudio.Text.Data (File: Microsoft.VisualStudio.Text.Data.dll)

· Microsoft.VisualStudio.Text.Logic (File: Microsoft.VisualStudio.Text.Logic.dll)

· Microsoft.VisualStudio.Text.UI (File: Microsoft.VisualStudio.Text.UI.dll)

· Microsoft.VisualStudio.Text.UI.Wpf (File: Microsoft.VisualStudio.Text.UI.Wpf.dll)

· System.ComponentModel.Composition (File: C:>\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ComponentModel.Composition.dll)

With the references added to the Project open the MultiSlotlipboardPackage.cs file and insert the following references on the beginning of the file.

using Microsoft.VisualStudio.Text.Editor;

using Microsoft.VisualStudio.TextManager.Interop;

using EnvDTE;

Additionally, open the MyControl.xaml.cs and insert the following references on the beginning of the file.

using System.Windows.Markup;

using System.Reflection;

using System.Xml;

using System.Runtime.Serialization.Formatters.Binary;

using System.IO;

using EnvDTE;

using Microsoft.VisualStudio.Editor;

using Microsoft.VisualStudio.Text.Editor;

using Microsoft.VisualStudio.TextManager.Interop;

This finishes the required steps to setup the project. Test the project by compiling and running it. You should see a new instance of Visual Studio open with a new window on the top bottom part of the interface, just like the Figure bellow. Note the title of this Visual Studio instance: Experimental Instance. The Visual Studio that has the project is on running mode and when you close the Experimental Interface the extension stops running. Make sure that you know exactly which Visual Studio you are working on to avoid mistakes.

Step 3: The User Interface

Next, let’s see how we create the user interface for the control.

We UI for the project is going to be very simple: we need a template
grid that will contain five controls: a label to identify the slot number, Cut, Copy and Paste buttons and a ListView that will show the history for each slot.

We will create a template grid will all these controls. Inside the code we will dynamically create the same grid and its controls five times. But first we need to delete the existing grid and button on the Mycontrol.xaml file, which has the following default layout.

We need to change the file so it will contain our template grid with the label, buttons and ListView. Therefore, the new grid will be just like the following layout:

What we did on the previous layout was first name the external grid grdMain. Next, we created the template grid named grdTemplate that has some columns for the layout. The other controls are the Paste and Copy buttons, named bntSlotPaste and bntSlotCopy, respectively. The ListVview named lstSlot1_Copy is created next followed but the label lblSlot2 and the Cut button bntSlotCut. The names and order of the controls inside the grid grdTemplate are very important and will be used on the next steps of this Instructable. The following Figure show how this interface looks like when we run the project.

We will clone the template grid on our code on a future step. But before that we need to add some methods on the package class MultiSlotClipboardPakage.

Step 4: Package Methods

The package class created by the Wizard needs some fields and methods to

allow our control to access in runtime the internal object that handle the text shown in code editor. We also need some access methods.

Open the file MultiSlotClipboardPackage.cs file and insert the static field named thePackage just after the definition of the class:

static public MultiSlotClipboardPackage thePackage;

Now find out the Initialize() method and insert the following line at the end of this method to get the reference of the object of this class.

MultiSlotClipboardPackage.thePackage = this;

Make sure to NOT change anything that is already there on the Initialize() method, which should look like this:

protected override void Initialize()

{

Debug.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));

base.Initialize();

// Add our command handlers for menu (commands must exist in the .vsct file)

OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;

if ( null != mcs )

{

// Create the command for the tool window

CommandID toolwndCommandID = new CommandID(GuidList.guidMultiSlotClipboardCmdSet, (int)PkgCmdIDList.cmdidMyTool);

MenuCommand menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID);

mcs.AddCommand( menuToolWin );

}

MultiSlotClipboardPackage.thePackage = this;

}

Next, we need to add two methods that will expose the text manager and DTE objects. Inside the class MultiSlotClipboardPackage add the following two methods:

public IVsTextManager getTextManager()

{

return (IVsTextManager) GetService(typeof(SVsTextManager));

}

public DTE Dte

{

get { return (DTE)GetService(typeof(DTE)); }

}

This finishes the modifications needed on the Package class MultiSlotClipboardPackage. Now we will focus our efforts on the MyControl class to get the selected text and insert what’s inside the slot on the source code file.

Step 5: Slot Data Structures and Methods

This step will create the data structure needed to store the data on the

extra slots. But first, open the MyControl.xaml.cs file and remove the method button1_Click() created by the wizard to show a message box when the original button was there. Since we removed the button from the XAML file we can erase that method. After erasing the button1_Click() method the MyControl class should be almost empty and look like this:

public partial class MyControl : UserControl

{

public MyControl()

{

InitializeComponent();

}

}

Let´s create some private fields for this class to store the data. First, we will create two constants that will store the maximum number of slots (5) and the maximum number of history that each slot have (10).

private const int MAX_SLOTS = 5;

private const int MAX_SLOT_HISTORY = 10;

Next, we need a field to store the view that allow the access to the current code window. This variable has the datatype of an interface: iWpTextView.

private IWpfTextView view;

The following field is the data structure that will contain all the data for the slots. It is an array where each element is a list of strings. The size of the array is obtained from the MAX_SLOTS constant.

private List[] aClipboadSlotData = new List[MAX_SLOTS];

These four fields are all that we need so far. Now we need to initialize the aClipboardSlotData with empty strings. We will do that on a new method called InitializeClipBoard(), which has the following source code:

// Sets the initial values of the data structure aClipboadSlotData

private void IntilializeClipBoard()

{

for (int i = 0; i < MAX_SLOTS; i++)

{

aClipboadSlotData[i] = new List();

for (int j = 0; j < MAX_SLOT_HISTORY; j++)

{

aClipboadSlotData[i].Insert(j, "");

}

}

}

The content of the InitializeClipBoard() is basically two nested for loops. The external for loop use the i integer variable to go to each element of the array, which are the slots. For each slot we instantiate a new list of strings. The internal for loop insert an empty string on aClipboadSlotData[i] the using the Insert() method of the List datatype.

We must call the InitializeClipBoard() inside the constructor of the MyControl class just after the call to InitializeComponent(). So far, the MyControl class looks like this.

public partial class MyControl : UserControl

{

private const int MAX_SLOTS = 5;

private const int MAX_SLOT_HISTORY = 10;

private IWpfTextView view;

private List[] aClipboadSlotData = new List[MAX_SLOTS];

public MyControl()

{

InitializeComponent();

// Add the empty slots

IntilializeClipBoard();

}

// Sets the initial values of the data structure aClipboadSlotData

private void IntilializeClipBoard()

{

for (int i = 0; i < MAX_SLOTS; i++)

{

aClipboadSlotData[i] = new List();

for (int j = 0; j < MAX_SLOT_HISTORY; j++)

{

aClipboadSlotData[i].Insert(j, "");

}

}

}

}

Step 6: Reading the Selected Text

The extra clipboard slots will behave just like the regular clipboard: the

user selects a text on the code window and copy (or cut) the text. This action copy the selected text to the internal memory storage. After that the user can move the cursor (caret) and paste the value whenever he/she wants.

To capture the selected text we need first to get the current code window. We do that by creating a new method named GetActiveTextView(), which return an object of the datatype IWpfTextView that will be stored on the view internal field created on the previous step. The code for the GetActiveTextView() is show bellow:

private IWpfTextView GetActiveTextView()

{

IWpfTextView view = null;

IVsTextView vTextView;

var txtMgr = MultiSlotClipboardPackage.thePackage.getTextManager();

txtMgr.GetActiveView(1, null, out vTextView);

var userData = vTextView as IVsUserData;

if (null != userData)

{

object holder;

var guidViewHost = DefGuidList.guidIWpfTextViewHost;

userData.GetData(ref guidViewHost, out holder);

var viewHost = (IWpfTextViewHost)holder;

view = viewHost.TextView;

}

return view;

}

This method starts by defining some internal variables. The view variable will be returned if the active view is the code window; otherwise, it will contain null. The vTextView is an auxiliary variable and the txtMgr gets the current view from the Package class MultiSlotClipboardPackage using the getTextManager() created on the Step 4.

This method need to check if the active Window is actually the code Window. We do that by calling the GetData() method of the IVsUserData interface. The rest of the code is just casting the variable to the correct datypes in a way that we have an object that represents the TextView of the editor that has the source code.

Now that we have a method to obtain the current text code view we need to extract the text that is selected. We do that by using three new methods: GetTextForPastie(), SelectionIsAvailable() and GetSelectedText().

// Get the SelectedText

private static string GetTextForPastie(ITextView view)

{

if (SelectionIsAvailable(view))

return GetSelectedText(view);

else

return "";

}

// Check to see if there is selected text

private static bool SelectionIsAvailable(ITextView view)

{

if (view == null)

throw new ArgumentNullException("view");

return !view.Selection.IsEmpty && view.Selection.SelectedSpans.Count > 0;

}

// Get the current selected text

private static string GetSelectedText(ITextView view)

{

return view.Selection.SelectedSpans[0].GetText();

}

The GetTextForPastie() method receive the view as a parameter and check if there is any selected text by calling the SelectionIsAvailable() method, which returns a boolean value that indicates if there is any selected text. The selected text is then obtained inside the GetSelectedText() that used the Selection property of the view object to get the text of the first SelectedSpan. The following link contains the documentation of the ITextSelection interface, which is the datatype of the Selection property.

https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.text.editor.itextselection.aspx

Step 7: Replicating the UI

So far we created some fields to store the data and some methods to

initialize the data structure and get the selected text. Now we must dynamically create the user interface to show five sets of the template grid.

We need to store the five grids on an array so we can access the grid using an index inside the control’s events. To store the dynamically created grids we need to create the aGroupGrids array as private field of the MyControl class. The datatype of this array is Grid (System.Windows.Controls.Grid) and maximum number of elements is stored on the constant MAX_SLOTS.

private Grid[] aGroupGrids = new Grid[MAX_SLOTS];

Before we initialize the array of grids we have to create a method to clone the existing grid grdTemplate we created on Step 3. Unfortunately, the .NET framework does not have a built in function to clone a UI control, so we have to create our own. Therefore, we must create the CloneControls() method as show bellow.

private Grid CloneControls(Grid myGrid)

{

string gridXaml = XamlWriter.Save(myGrid);

StringReader stringReader = new StringReader(gridXaml);

XmlReader xmlReader = XmlReader.Create(stringReader);

Grid newGrid = (Grid)XamlReader.Load(xmlReader);

return newGrid;

}

The CloneControls() method receive a Grid object as a parameter and returns another Grid object, which is a clone of parameter. Inside this method, we use the classes that serialize objects on the XML format to get all the properties, values and other elements stored inside the control. The following link contains a good explanation about XML serialization with the XamlWriter and XMlReader classes on the .NET Framework 4.0.

http://blogs.msdn.com/b/bursteg/archive/2009/05/18/xaml-in-net-4-0-serialization-and-deserialization-using-xamlservices.aspx

Now that we have the aGroupGrids array to store the dynamic created controls and the CloneControls() method to clone the grid objects we need to initialize the elements, i.e. create them. We will do that inside a new method called InitializeInteface(). This method should be called inside the MyControl’s constructor just after the InitializeClipboard() call. The constructor of the MyControl class should look like this:

public MyControl()

{

InitializeComponent();

// Add the empty slots

IntilializeClipBoard();

// Initilizae the interface

InitializeInterface();

}

Inside the InitializeInterface() we should do the following tasks:

· Hide the grdTemplateGrid;

· For each new slot clone the grdTemplateGrid grid:

o Insert the new cloned grid on the aGroupGrid array.

o Set the visibility for the new cloned grid.

o Set the position of the cloned grid to a new margin so the grids do not overlap visually.

o Change the caption of the label to show which grid correspond to each slot.

o Set the Tag property of the controls and the ListView to store the index of the cloned grid.

o Set the delegate methods to the click events of the buttons. Also, set the delegate method to the double click event of the ListView.

o Add the new cloned grid to the container grid grdMain.

The complete code for the InitializeInterface() method is shown below:

private void InitializeInterface()

{

Double left_initial = -170;

this.grdTemplate.Visibility = Visibility.Hidden;

for (int i = 0; i < MAX_SLOTS; i++)

{

aGroupGrids[i] = CloneControls(this.grdTemplate);

aGroupGrids[i].Visibility = Visibility.Visible;

left_initial = left_initial + 180.0;

aGroupGrids[i].Margin = new System.Windows.Thickness(left_initial, -5.0, 0.0, 0.0);

// Need to change the Slot label + add the events to the buttons and the list

// Order of elements

// 0 -> Button Paste

// 1 -> Button Copy

// 2 -> ListView

// 3 -> Label

// 4 -> Button Cut

aGroupGrids[i].Tag = i;

// Set the Label caption

((Label)aGroupGrids[i].Children[3]).Content = "Slot " + (i + 1).ToString();

// Set the Tags properties with the index of this grid in the array

// This is gonna be usefull latter on (for the events)

Button Paste = ((Button)aGroupGrids[i].Children[0]);

Button Copy = ((Button)aGroupGrids[i].Children[1]);

Button Cut = ((Button)aGroupGrids[i].Children[4]);

ListView lstData = ((ListView)aGroupGrids[i].Children[2]);

Paste.Tag = i;

Copy.Tag = i;

Cut.Tag = i;

lstData.Tag = i;

// Set the Buttons Events (Click)

Copy.Click += Copy_Click;

Paste.Click += Paste_Click;

Cut.Click += Cut_Click;

// Set the ListView Event (MouseDoubleClick)

lstData.MouseDoubleClick += lstData_MouseDoubleClick;

// Add this grid to the parent Grid

grdMain.Children.Add(aGroupGrids[i]);

}

}

How all we have to do is fill in the delegate methods for the buttons (Copy_Click, Pastle_Click, Cut_Click) and the method for the double click on the ListView (lstData_MouseDoubleClick), which will show a simple MessageBox with the content of the selected item.

Step 8: Coding the Events

In this step we need to fill in the code for the events of the buttons

and the ListView. These events are generic, which means that all the dynamically generated controls will call these events. So, to know which slot called the event we will read the Tag property of the control, which contains the correct index of the array aClipboardSlotData.

Let’s start with the event that handles the double click on the ListView. When the user double click a ListViewItem we should show a message box with the content of the selected elements.

If the SelectedIndex of the ListView property is greater than -1 this means that an item was selected and we will be use the value of this property as an index for the array that store the clipboard data (aClipboardSlotData). The following code shows the content of the lstData_MouseDoubleClick() delegate method.

void lstData_MouseDoubleClick(object sender, MouseButtonEventArgs e)

{

ListView v = (ListView)sender;

int index = Convert.ToInt32(v.Tag);

if (v.SelectedIndex != -1)

MessageBox.Show(aClipboadSlotData[index].ElementAt(v.SelectedIndex).ToString());

}

The next delegate method we need to create is the event that handle the click on the Copy button. This delegate method is called Copy_Click() and we need to first get the active code window with the GetActiveView() method created on Step 6 and check if the view is the correct one.

Next, we must read the current selected text with the GetTextForPastie() method that was also created on Step 6. After we get the text we need to check if it is empty.

If the selected text is not empty, we need to insert it on the array aClipboardSlotData. We perform this insertion in a new method called InsertDataHistory(). Finally, we must update the ListView using another new method called AccomodateDataListView(). But first let’s see the code for the Copy_Click() delegate method.

void Copy_Click(object sender, RoutedEventArgs e)

{

int index = Convert.ToInt32(((Button)sender).Tag);

view = GetActiveTextView();

if (!(this.view == null || MultiSlotClipboardPackage.thePackage.Dte.ActiveDocument == null))

{

String sData = GetTextForPastie(view);

if (sData != "")

{

InsertDataHistory(index, sData);

AccomodateDataListView(index);

}

}

}

The Copy_Click() starts by reading the value of the Tag property of the button that was clicked. This property contain the index that we are going to use as the slot number. Next we call the GetActiveTextView() method and check the return value to see if we got the correct window.

The following lines of code get the selected text with the GetTextForPastie() method. We then check to see if there is any selected text and if so we call the InsertDataHistory() method to insert the data on the aClipboardSlotData array. Finally, we change the elements of the ListView with the AccomodateDataListView() method.

The InsertDataHistory() method has a simple job. First, it receives the index of the clipboard slot and the selected text. Then is should insert the selected text on the first position of the aClipboardSlotData array, which should work like a stack or LIFO data structure (http://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29).

Instead of implement a stack data structure from scratch we just create a new List and insert the selected text value sent as a parameter in its first position. Next, we traverse the existing aClipboardSlotData array starting from the second element and inserting the data on the new created List. Finally, we override the existing List on aClipboardSlotData with the one we just created. The following code shows how these operations are performed.

private void InsertDataHistory(int indexSlot, String sData)

{

List list_temp = new List();

list_temp.Insert(0, sData);

for (int i = 0; i < aClipboadSlotData[indexSlot].Count - 1; i++)

{

list_temp.Insert(i + 1, aClipboadSlotData[indexSlot].ElementAt(i).ToString());

}

aClipboadSlotData[indexSlot].RemoveRange(0, aClipboadSlotData[indexSlot].Count);

aClipboadSlotData[indexSlot].InsertRange(0, list_temp);

return;

}

Now that we inserted the selected text on the correct position of the List stored inside the aClipboardSlotData array we must update the elements of the ListView. We do that inside the AccomodateDataListView() method that receive the index of the array as a parameter.

The AccomodateDataListView() is straightforward: we erase all the ListViewItem elements and inserted new ones in the order of the List stored inside the aClipboardSlotData. We just make sure to insert only the 15 characters of each text to avoid cut the text. Finally, we ask for a refresh on the UI and select the first element of the ListView.

private void AccomodateDataListView(int indexSlot)

{

ListView v = (ListView)aGroupGrids[indexSlot].Children[2];

v.Items.Clear();

foreach (String s in aClipboadSlotData[indexSlot])

{

if (!s.Equals(""))

{

if (s.Length > 15)

v.Items.Add(s.Substring(0, 15));

else

v.Items.Add(s);

}

}

v.Items.Refresh();

v.SelectedIndex = 0;

}

We are almost done. We need to create the code for the events that handle the click in the Cut and the Paste buttons. The event for the Cut button is almost the same as the Copy button: the only difference is that after we get the selected text we need to erase the text that is selected from the code window and remove the text’s selection.

To erase the selected text we must use the object that implement the ITextEdit interface. We get this object from the CreateEdit() method of the TextBuffer property found on the view variable that represents the current code editor view. Once we get the ITextEdit object we use the Delete() method to erase the characters and the Apply() method to change the text. Finally, we remove the selection using the Clear() method of the Selection property of the view.

But there is a catch when we use the Delete() method: we must provide the position we want to start deleting the characters and how many characters we need to delete. To get the position of the current selection we use the Position property from the Selection.Start property, which represents the start place where selection begin. In a similar way, we use the Selection.End property to get the exact integer position of the end of the selection. The code for the delegate method Cut_Click() is shown below.

void Cut_Click(object sender, RoutedEventArgs e)

{

int index = Convert.ToInt32(((Button)sender).Tag);

view = GetActiveTextView();

if (!(this.view == null || MultiSlotClipboardPackage.thePackage.Dte.ActiveDocument == null))

{

String sData = GetTextForPastie(view);

if (sData != "")

{

InsertDataHistory(index, sData);

AccomodateDataListView(index);

Microsoft.VisualStudio.Text.ITextEdit edit = view.TextBuffer.CreateEdit();

edit.Delete(view.Selection.Start.Position.Position, view.Selection.End.Position.Position - view.Selection.Start.Position.Position);

edit.Apply();

view.Selection.Clear();

}

}

}

The final delegate method we must create is the one that handle the click event on the Paste button. However, before we create this method we must first develop a way to extract the text element from the array aClipboardSlotData, which is the data structure that store the slots and their history.

The new method that extracts the text from a slot history is called getSelectedItem() and it receives the index of the slot as a parameter. This method must return the data value from the slot’s history that corresponds to the selected item on the ListView. If no item is selected on the ListView the method should return the first element of the ListView.

The first task of the getSelectedItem() is to get the corresponding ListView with the index and the aGroupGrids array. Next, the method verifies if the obtained ListView is empty and if it is not the SelectedIndex property is used with the aClipboardSlodData array to get the element from the correct slot and the correct history position.

private String getSelectedItem(int indexSlot)

{

String ret = "";

ListView v = (ListView)aGroupGrids[indexSlot].Children[2];

if (v.Items.Count > 0)

{

if (v.SelectedIndex >= 0)

{

ret = aClipboadSlotData[indexSlot].ElementAt(v.SelectedIndex).ToString();

}

else

ret = aClipboadSlotData[indexSlot].ElementAt(0).ToString();

}

return ret;

}

Finally, the last piece of code we must implement is the delegate method that handle the click on the Paste button. The method Paste_Click() first get the current view using the GetActiveTextView() and check if the current view is the code editor, just like the Copy_Click() and Cut_Click() delegate methods. The method then calls the getSelectedItem() sending as a parameter the index valued obtained from the Tag property of the button that was clicked.

If there is some data on the corresponding history and slot position, this text must be inserted in the current cursor (caret) position. To insert the text value in the exact cursor position we use again the object that implement the ITextEdit interface obtained from the CreateEdit() method of the TextBuffer property, just like we did on the Cut event method. However, here we should use the Insert() method instead of the Delete() and provide the current carret position obtained from the Position property of the Caret.Position.BufferPostion property of the view object. The final piece of code of this Instructable is show below

void Paste_Click(object sender, RoutedEventArgs e)

{

int index = Convert.ToInt32(((Button)sender).Tag);

view = GetActiveTextView();

if (!(this.view == null || MultiSlotClipboardPackage.thePackage.Dte.ActiveDocument == null))

{

String sData = getSelectedItem(index);

if (!sData.Equals(""))

{

Microsoft.VisualStudio.Text.ITextEdit edit = view.TextBuffer.CreateEdit();

edit.Insert(view.Caret.Position.BufferPosition.Position, sData);

edit.Apply();

}

}

}

Step 9: Testing

Now that we finished the coding let’s start or Visual Studio extension.

Compile and Run the project and check the Window shown on the new instance of Visual Studio that appears. The control we develop should look like the following Figure.

Open a new project and start testing the cut, copy and paste buttons
when working with some code. Note that only the selected value that from a ListView is pasted on the code area.

Coded Creations

Participated in the
Coded Creations