The LineChanged Event
The automation object model defines a single event specific to editing: the LineChanged event. Having only one text-editing event at your disposal is both a blessing and a curse, analogous to owning a single flat screwdriver when half the world is built using Phillips screwsyou always know which tool you're going to use and you can disassemble just about anything, given time, but you sometimes find yourself wishing for a more varied toolbox.The LineChanged event has three parameters to help you figure out why the event fired. The first two parameters, StartPoint and EndPoint, mark the beginning and end of the changes to the text buffer. You can use these TextPoint values to retrieve the changes, like so:
Dim text As String
text = StartPoint.CreateEditPoint.GetText(EndPoint)
And if you're ever curious about which document the changes belong to, you can follow the object hierarchy up a couple of levels to find the parent document:
Dim doc As Document
doc = StartPoint.Parent.Parent
The third parameter, Hint, is a bit flag that holds values from the vsT-extChanged enumeration (shown in Table 11-8). The flags set in Hint are evidence from the crime scene that you can piece together to recreate the actions leading up to the event. (In practice, the Hint parameter doesn't give you quite enough information to figure out exactly what led to the eventbut then, if it did, it wouldn't be called a hint.)
Field | Description |
---|---|
vsTextChangedMultiLine | The changes affected multiple lines of text. |
vsTextChangedSave | The changes were saved to disk. |
vsTextChangedCaretMoved | The insertion point moved off the line containing the changes. |
vsTextChangedReplaceAll | The entire text buffer was replaced by an insertion. |
vsTextChangedNewLine | A new line was entered. |
vsTextChangedFindStarting | A find operation moved the insertion point off the line containing changes. |
Listing 11-2 Testing the effect of undo contexts on the LineChanged event
UndoContexts and LineChanged Events
Private Const title As String = "Text Editing Events"
<System.ContextStaticAttribute()> Public WithEvents _
TextEditorEvents1 As EnvDTE.TextEditorEvents
Sub LineChangedAndUndoContexts(Optional ByVal useUndoContext _
As String = "False")
' Description: Demonstrates the effect of undo contexts on the
' LineChanged event
TextEditorEvents1 = DTE.Events.TextEditorEvents()
Dim useUndo As Boolean = ToBoolean(useUndoContext)
' Create a new document
Dim txtDoc As TextDocument = GetTextDocument("LineChanged Test")
Dim editPnt As EditPoint = txtDoc.EndPoint.CreateEditPoint
' Insert text. Not a new line, so won't trigger LineChanged
editPnt.Insert("Beginning of document.")
If useUndo Then
Try
DTE.UndoContext.Open("LineChangedAndUndoContexts")
' Inside undo context, so new lines won't trigger
' LineChanged until the undo context closes
editPnt.Insert(Environment.NewLine)
editPnt.Insert("Here's a new line." + Environment.NewLine)
DTE.UndoContext.Close()
Catch
End Try
Else
' No undo context, so new lines trigger LineChanged immediately
editPnt.Insert(Environment.NewLine)
editPnt.Insert("Here's a new line." + Environment.NewLine)
End If
End Sub
' LineChanged event handler
Public Sub TextEditorEvents1_LineChanged( _
ByVal StartPoint As EnvDTE.TextPoint, _
ByVal EndPoint As EnvDTE.TextPoint, _
ByVal Hint As Integer) Handles TextEditorEvents1.LineChanged
Dim output As New OutputWindowPaneEx(DTE, title)
output.WriteLine("--- TextEditorEvents1_LineChanged ---")
output.WriteLine("Changed text:")
output.WriteLine(StartPoint.CreateEditPoint.GetText(EndPoint))
output.WriteLine()
End Sub
Function ToBoolean(ByVal val As Object) As Boolean
Dim bool As Boolean = False
Try
' Convert the bool parameter
bool = System.Convert.ToBoolean(val)
Catch
End Try
Return bool
End Function
The LineChangedAndUndoContexts macro in Listing 11-2 creates a new document and adds a couple lines of text to it. Figure 11-5 shows the result of running this macro without using undo contextsthe LineChanged event fires once for each new line of text, which is the same as if you had typed in the lines by hand. Figure 11-6 shows what happens when you wrap the changes in an undo context. Because the new lines are created within an undo context, all the changes, including the ones before the undo context started, get rolled into one LineChanged event that fires after the undo context closes.
Figure 11-5. Text changes without an undo context generate two LineChanged events.

Figure 11-6. Text changes with an undo context generate one LineChanged event.

Multiple LineChanged Event Handlers
A LineChanged event handler can modify the text buffer without triggering LineChanged events, which allows you to add changes without fear of reentry. When multiple event handlers are involved, any changes that one event handler makes are seen by all event handlers downstream; the same Hint parameter gets passed along to each succeeding event handler, but the StartPoint and EndPoint parameters expand or contract to include any changes made by previous event handlers.
Lab: Using Multiple LineChanged Event HandlersThe MultipleHandlers add-in included with the book's sample files lets you experiment with the changes that pass down the chain of multiple LineChanged event handlers. MultipleHandlers defines three LineChanged event handlers, whose names reflect the order in which they fire: LineChanged1, LineChanged2, and LineChanged3. (Note that an add-in's event handlers fire in the same order in which they subscribe to the event. Multiple macro event handlers, however, fire in reverse order of declaration.) Each of these event handlers displays information about its parameters; in addition, LineChanged1 and LineChanged2 modify the document so you can see how the changes are passed along to LineChanged2 and LineChanged3, respectively.MultipleHandlers also defines a command named ReplaceAll, which accepts an optional Boolean parameter that controls the changes made by LineChanged2. Passing a False parameter (or no parameter) to ReplaceAll directs LineChanged2 to add a line of text, whereas a True parameter causes LineChanged2 to replace all text in the document with the document's last two lines. Note that one call to ReplaceAll buys you one LineChanged eventLineChanged3 unsubscribes the event handlers so you don't accidentally modify important files when you're done with the lab.Here's experiment #1:Open a new text file.In the Command Window, type MultipleHandlers.Connect-.--ReplaceAll and press Enter.In the text file, type in a line of text (such as Here's a line) and press Enter. Pressing Enter triggers the LineChanged event, and the Text Editing Event Output Window pane displays the event handlers' output. You'll see that the changed text grows from LineChanged1 to LineChanged3 as new text is added by intervening event handlers.Experiment #2 demonstrates the constancy of the Hint parameter:Open a new text file and add three lines of text.In the Command Window, type MultipleHandlers.Connect.-ReplaceAll and press Enter.Run the TextEditingEvents.ReplaceTextWithLastTwoLines macro. In the Text Editing Event Output Window pane, verify that one of the hints is vsTextChangedReplaceAll. The ReplaceTextWithLastTwoLines macro copies the last two lines of text in a document and then replaces all the text in the document with those two lines. LineChanged passes along the vsTextChangedReplaceAll value to let you know that a wholesale replacement triggered the event. But see what happens when MultipleHandlers performs the same operations in its LineChanged2 event handler:Open a new text file and add two lines of text.In the Command Window, type MultipleHandlers.Connect.-ReplaceAll True and press Enter.Add a third line of text and press Enter. When you examine the output, you'll see that every event handler received vsTextChangedCaretMoved and vsTextChangedNewline as the hints for the event, even though LineChanged2 performed actions that otherwise would have garnered a vsTextChangedReplaceAll. Keep this in mind if you're tempted to use the Hints parameter in your event handler's logicif your event handler isn't first in line, the information in Hints might be as stale as month-old biscotti. |