At the risk of stating the obvious (and possibly the painfully obvious), the Visual Studio 2005 text editor operates on documents. When you program, it's easy to think that you're typing in a file; you load source code from a file when you begin editing and you save the changes to a file when you finish, so it's natural to assume that all the time in between is spent working on a file. However, a file is something that exists on disk—the document you work with in the text editor is something less permanent but infinitely more malleable. This section introduces you to the two objects that capture these qualities of documents and make them available to you through automation: the Document and TextDocument objects.
The Document object serves as a general-purpose wrapper for text data; it provides methods and properties that give you high-level control over both the data and the windows in which that data appears.
You can create a document programmatically by using methods of the ItemOperations object, which is covered in Chapter 7, and the ProjectItems object, which is covered in Chapter 8. For example, the ItemOperations.NewFile method, which corresponds to the New command on the File menu, lets you create a file that isn't associated with a particular project. The following macro shows how to create a text file by using the NewFile method:
Sub CreateNewTextFile() ' Description: Shows how to use the ItemOperations.NewFile method ' to create a new text file Dim Item As String = "General\Text File" Dim Name As String = "MyTextFile" Dim ViewKind As String = Constants.vsViewKindPrimary Dim win As Window win = DTE.ItemOperations.NewFile(Item, Name, ViewKind) End Sub
One peculiarity of the NewFile method's Name parameter is that it specifies the name of the new document indirectly. With an existing file, the document name and the window caption both correspond to the file name. With a document created by NewFile, however, the Name parameter serves as the caption of the new document's window only—the document acquires the name of the temporary file created by Visual Studio 2005 to store the new document. The "indirectly" part happens when you save the document; Visual Studio 2005 displays the Name value as the default name of the file to save.
You have three main ways of finding and retrieving an existing Document object: the DTE. Documents collection, the Window.Document property, and the DTE.ActiveDocument property. The DTE.Documents collection contains a reference to every open Document object. Just as with any other collection in the automation object model, you can iterate through the Document objects in the collection looking for the one you want, or, if you know the name of the document, you can retrieve it by using the Documents.Item method, like so:
Dim doc As Document = DTE.Documents.Item("MyFile.cs")
If you have a Window object, its Document property returns the associated Document object. Some of the tests for this chapter use the following macro to retrieve the Document object of a Window; if the window doesn't exist, the macro creates a new text file with the requested caption and returns its Document object:
Function GetDocument(ByVal caption As String) As Document ' Description: Retrieves the Document object associated with ' the specified window, or creates a text file in ' a new window and returns its Document object Dim win As Window Try win = DTE.Windows.Item(caption) Catch ex As System.Exception win = DTE.ItemOperations.NewFile("General\Text File", caption) End Try Return win.Document End Function
The relationship of Document objects to windows is one-to-many; a window always has one associated Document, but a Document can be open in many windows. You can open a new window on a document by using the Document.NewWindow method, which works in the same way as the New Window command on the Window menu. Each of the windows associated with a particular document will have as its caption the document name followed by a colon (:) and the window number, for example, Connect.cs:1, Connect.cs:2, and so on. Because the windows
Warning | Visual Basic files don't support Document.NewWindow, so they throw a "not implemented" exception when you call this method. |
The ability to have multiple windows means that you won't find a Document.Parent property that returns the containing window. (Which window would it return?) Instead, you can find all the windows associated with a particular document by iterating through the Document. Windows collection, as shown by the following macro:
Sub ListDocumentWindows() ' Description: Lists all the windows associated with ' each open document Dim pane As OutputWindowPane = _ GetOutputWindowPane("List Document Windows") pane.Clear() Dim doc As Document For Each doc In DTE.Documents Dim win As Window pane.OutputString(doc.Name & " windows:" & vbCrLf) For Each win In doc.Windows pane.OutputString(" " & win.Caption & vbCrLf) Next pane.OutputString(vbCrLf) Next End Sub
You can find the active window for the Document object by using its ActiveWindow property, which returns the active window, if applicable, or the topmost window associated with the document if none of the document's windows is active.
Warning | The Document.ActiveWindow property has a bug—it always returns the first document window, regardless of which window has the focus. |
The coarsest means available to the Document object for managing changes is its ReadOnly property, which allows you to get or set the document's read-only state. Methods that modify a document's text throw an exception if the document is read-only, so it's worth checking the ReadOnly property before you make text changes.
You can undo and redo changes to a document by using the Document.Undo and Document. Redo methods, respectively. These two methods offer the same functionality as their Edit menu counterparts. The Undo and Redo methods both return a Boolean value indicating whether the operation took place.
Warning | You can call Document.Undo or Document.Redo on a read-only document as many times as you want to, so long as the corresponding undo or redo stack is empty; in such cases, the method returns False. The problem is that you can change the Document. ReadOnly property on the fly, which means you can have undoable (or redoable) changes in your document when you switch from read-write to read-only. If you call Undo or Redo on a nonempty undo or redo stack of a read-only document, you get an exception. |
The Document.TextSelection property returns the TextSelection object associated with the active window, or the topmost window if none of the document's windows has the focus. You can use the TextSelection object's myriad editing methods and properties to automate just about any editing task. (You'll learn all about TextSelection objects in the upcoming section titled "The TextSelection Object.")
The Document.Save method saves the document and optionally lets you choose the name and the location to save to. The Save method throws an exception if the location you specify doesn't already exist; if you give a correct location but no name, the Save method uses the current name of the document. (A bug in the Microsoft Visual C++® implementation causes the Save method to ignore any new file name that you give it.) If you want to save every open document in one call, use the SaveAll method of the Documents collection. What you gain in convenience you give up in control—you can't specify new names or locations for the files as you can with the Save method.
The Document.Close method closes a document and also lets you pass in a vsSaveChanges value that signals whether to save changes (vsSaveChanges), discard changes (vsSaveChangesNo), or let the user decide whether to save changes (vsSaveChangesPrompt, which is the default). The Documents collection has a corresponding CloseAll method that lets you close every document and also specify a vsSaveChanges value to apply to every document.
The Document.Saved property indicates whether the document has changes that haven't yet been saved—a value of False means that the document has unsaved changes, as indicated by an asterisk in the document window's title bar. Essentially, this property controls whether the IDE prompts you to save a document when the document is closed. You can write to this property, but be aware that you will lose any unsaved changes if you close a document after setting its Saved property to True.
Whereas a Document object can represent any document in the IDE, the TextDocument object represents text documents only. You retrieve a TextDocument object by using the Document. Object method and passing in an empty string or a value of "TextDocument"; the method returns null or Nothing for nontext documents.
The most important TextDocument properties and methods are those related to the TextPoint, EditPoint, and TextSelection editing objects. The TextDocument.Selection property returns the text document's selection and behaves the same as the Document.Selection property. The StartPoint and EndPoint properties return TextPoint objects that mark the beginning and end, respectively, of the text document buffer. The CreateEditPoint method returns an EditPoint object at the location of the TextPoint passed into the method; passing in null or Nothing creates an EditPoint at the beginning of the document.
Note | It makes little sense to call TextDocument.CreateEditPoint with a TextPoint parameter because a TextPoint object already has its own CreateEditPoint method. However, passing null to TextDocument.CreateEditPoint is the only way to create an EditPoint without first creating an intermediary point object. |