Monday, July 2, 2007

Adding text to the Clipboard from a Macro - DTE.ExecuteCommand("Edit.Cut")

On 2 occasions now my adventures have required that I write a vb.net macro that placed some text into the clipboard. The first adventure involved having the user type "check-in comments" into an inputbox, writing those comments into the code and then placing the comments into the clipboard so they could use them as check-in comments in the check-in window (which I launched as part of the macro). That first adventure proved treacherous because no matter how hard I pounded the same nail with the same hammer it didn't want to go in.

Whenever my macro code tried to execute this line:

Clipboard.SetText(originalComment, TextDataFormat.Text)

I would get this error:
"Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it."

Finally, I came across the workaround of placing the text into the code editor in VS and then executing this line of code:

DTE.ExecuteCommand("Edit.Cut")

This worked wonders because I had tricked Visual Studio into thinking the user had clicked Cut in the Edit menu.

Today brought a new challenge. I needed to insert many lines into the user's clipboard. In my single line situation I had used the following to select my single line.

DTE.ActiveDocument.Selection.LineUp
DTE.ActiveDocument.Selection.SelectLine


It turns out the .LineUp() method supports 2 optional parameters. The first (extend as boolean) tells the editor whether or not to make a selection of the lines as it moves up. The second parameter [count as integer] tells the editor how many lines to go up.

Here's my final solution for copying text to the user's clipboard:
This requires the following imports
Imports System.Text.RegularExpressions
Imports EnvDTE
Imports EnvDTE80


'copies the given text to the IDE clipboard
Friend Sub CopyTextToClipboard(ByVal textToCopy As String)
  Dim sel As TextSelection = DTE.ActiveDocument.Selection
  With sel
    DTE.UndoContext.Open("ToClipboard") 'an arbitrary name

    Dim mc As MatchCollection = Regex.Matches(textToCopy, "\n")
    Dim lineCount As Integer = mc.Count + 1 'add one for the NewLine (below)

    'add the text into the VS code editor
    .Text = textToCopy & ControlChars.NewLine

    'now select the text that we pasted
    .LineUp(True, lineCount) 'True creates a selection region

    'cut it to put it into THEIR clipboard
    DTE.ExecuteCommand("Edit.Cut") 'by using Edit.Cut it accesses the IDE Clipboard!

    DTE.UndoContext.Close()
  End With
End Sub



In this particular situation I frequently paste over 100 lines into the clipboard and watching Visual Studio load those lines into the IDE and then cut them right back out is a unnecessarily time consuming. If anyone knows a faster method for rendering text into the IDE or the clipboard I would love to hear from you.

Thanks for joining me on this adventure.

No comments: