VSTO Support for Word Schema Mapping
This section covers VSTO 2005's support for Word's schema mapping. Let's create a new VSTO 2005 Word project based on the book order document we created in this chapter. Launch VSTO 2005, and from the New menu in the File menu choose Project. In the New Project dialog, select a C# Word document project. Give the project a name and location, and then click the OK button. A dialog then appears asking for a document to be used for the application. Click the Copy an existing document radio button. Then click the "..." button to browse to the document you created in this chapter that has the book order schema mapped in it. Click the Finish button to create the project.
We want to consider several features of the generated VSTO project. First is the creation of XMLNode controls. Second is the creation of XMLNodes controls. Finally, we will consider how to use the UpdateXml methods on an XMLNode to load XML into our document without using an XSLT file.
Use the class view to browse the members associated with ThisDocument. Notice as you browse that the member variables listed in Table 22-1 have been created automatically based on the XML mapping in the document to the book order schema.
Name |
Type |
---|---|
OrderNode |
Microsoft.Office.Tools.Word.XMLNode |
OrderCustomerNameNode |
Microsoft.Office.Tools.Word.XMLNode |
OrderDateNode |
Microsoft.Office.Tools.Word.XMLNode |
OrderBookNodes |
Microsoft.Office.Tools.Word.XMLNodes |
BookTitleNodes |
Microsoft.Office.Tools.Word.XMLNodes |
BookISBNNodes |
Microsoft.Office.Tools.Word.XMLNodes |
BookPublisherNodes |
Microsoft.Office.Tools.Word.XMLNodes |
BookPriceNodes |
Microsoft.Office.Tools.Word.XMLNodes |
OrderSubtotalNode |
Microsoft.Office.Tools.Word.XMLNode |
OrderTaxNode |
Microsoft.Office.Tools.Word.XMLNode |
OrderTotalNode |
Microsoft.Office.Tools.Word.XMLNode |
The XMLNode Control
For each nonrepeating element mapped to the Word document, VSTO creates an XMLNode control. For example, by mapping the nonrepeating element CustomerName from the Order element, VSTO created an XMLNode control called OrderCustomerNameNode. An XMLNode control has all the properties and methods of a Word XMLNode object. In addition, it has several events that are not found on the Word XMLNode object:
- XMLNode.AfterInsert is raised when a new XML element is added to the document.
- XMLNode.BeforeDelete is raised when an XML element is removed from the document.
- XMLNode.ContextEnter is raised when the XML node has focus.
- XMLNode.ContextLeave is raised when the XML node loses focus.
- XMLNode.Select is raised when text within the XML node is selected.
- XMLNode.Deselect is raised when text within the XML node is deselected.
- XMLNode.ValidationError is raised when a validation error occurs within the XML node.
Listing 22-5 shows a VSTO customization that handles all the events associated with an XMLNode. In this case, the code handles events associated with the XMLNode called OrderCustomerNameNode, which corresponds to the CustomerName element from the book order schema mapped into the Word document.
Listing 22-5. A VSTO Word Customization That Handles All Events Associated with an XMLNode Control
using System; using System.Data; using System.Drawing; using System.Windows.Forms; using Microsoft.VisualStudio.OfficeTools.Interop.Runtime; using Word = Microsoft.Office.Interop.Word; using Office = Microsoft.Office.Core; namespace WordApplication1 { public partial class ThisDocument { private System.Windows.Forms.ListBox list; private void ThisDocument_Startup(object sender, EventArgs e) { this.OrderDateNode.AfterInsert += new Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventHandler( OrderDateNode_AfterInsert); this.OrderDateNode.BeforeDelete += new Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventHandler( OrderDateNode_BeforeDelete); this.OrderDateNode.ContextEnter += new Microsoft.Office.Tools.Word.ContextChangeEventHandler( OrderDateNode_ContextEnter); this.OrderDateNode.ContextLeave += new Microsoft.Office.Tools.Word.ContextChangeEventHandler( OrderDateNode_ContextLeave); this.OrderDateNode.Select += new Microsoft.Office.Tools.Word.NodeSelectionEventHandler( OrderDateNode_Select); this.OrderDateNode.Deselect += new Microsoft.Office.Tools.Word.NodeSelectionEventHandler( OrderDateNode_Deselect); this.OrderDateNode.ValidationError += new EventHandler(OrderDateNode_ValidationError); list = new System.Windows.Forms.ListBox(); ActionsPane.Controls.Add(list); ActionsPane.Show(); } #region VSTO Designer generated code private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } #endregion private void Display(string text, string text2) { list.Items.Add(String.Format("{0} {1}", text, text2)); } void OrderDateNode_AfterInsert(object sender, Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventArgs e) { Microsoft.Office.Interop.Word.XMLNode node = sender as Microsoft.Office.Interop.Word.XMLNode; Display("AfterInsert", node.BaseName); } void OrderDateNode_BeforeDelete(object sender, Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventArgs e) { Microsoft.Office.Interop.Word.XMLNode node = sender as Microsoft.Office.Interop.Word.XMLNode; Display("BeforeDelete", node.BaseName); } void OrderDateNode_ContextEnter(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("ContextEnter", e.NewXMLNode.BaseName); } void OrderDateNode_ContextLeave(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("ContextLeave", e.NewXMLNode.BaseName); } void OrderDateNode_Select(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("Select", e.Selection.Text); } void OrderDateNode_Deselect(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("Deselect", e.Selection.Text); } } }
The XMLNodes Control
For each repeating element mapped to the Word document, VSTO creates an XMLNodes control. For the repeating element Book from the Order element, VSTO created an XMLNodes control called OrderBookNodes. An XMLNodes control has all the properties and methods of a Word XMLNodes object. In addition, it has several events that are not found on the Word XMLNodes object:
- XMLNodes.AfterInsert is raised when a new XML element is added to the document.
- XMLNodes.BeforeDelete is raised when an XML element is removed from the document.
- XMLNodes.ContextEnter is raised when an element contained by the XMLNodes control gets focus.
- XMLNodes.ContextLeave is raised when the elements contained by the XMLNodes control loses focus.
- XMLNodes.Select is raised when text within the elements contained by the XMLNodes control is selected.
- XMLNodes.Deselect is raised when text within the elements contained by the XMLNodes control is deselected.
- XMLNodes.ValidationError is raised when a validation error occurs within the elements contained by the XMLNodes control.
Listing 22-6 shows a VSTO customization that handles all the events associated with an XMLNodes control. The code handles events associated with the XMLNodes control called OrderBooksNode, which corresponds to the repeating Book element from the book order schema that was mapped into the Word document.
Listing 22-6. A VSTO Word Customization That Handles All Events Associated with an XMLNodes Control
using System; using System.Data; using System.Drawing; using System.Windows.Forms; using Microsoft.VisualStudio.OfficeTools.Interop.Runtime; using Word = Microsoft.Office.Interop.Word; using Office = Microsoft.Office.Core; namespace WordApplication1 { public partial class ThisDocument { private System.Windows.Forms.ListBox list; private void ThisDocument_Startup(object sender, EventArgs e) { this.OrderBookNodes.AfterInsert += new Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventHandler( OrderBookNodes_AfterInsert); this.OrderBookNodes.BeforeDelete += new Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventHandler( OrderBookNodes_BeforeDelete); this.OrderBookNodes.ContextEnter += new Microsoft.Office.Tools.Word.ContextChangeEventHandler( OrderBookNodes_ContextEnter); this.OrderBookNodes.ContextLeave += new Microsoft.Office.Tools.Word.ContextChangeEventHandler( OrderBookNodes_ContextLeave); this.OrderBookNodes.Select += new Microsoft.Office.Tools.Word.NodeSelectionEventHandler( OrderBookNodes_Select); this.OrderBookNodes.Deselect += new Microsoft.Office.Tools.Word.NodeSelectionEventHandler( OrderBookNodes_Deselect); this.OrderBookNodes.ValidationError += new EventHandler(OrderBookNodes_ValidationError); list = new System.Windows.Forms.ListBox(); ActionsPane.Controls.Add(list); ActionsPane.Show(); } #region VSTO Designer generated code private void InternalStartup() { this.Startup += new EventHandler(ThisDocument_Startup); } #endregion private void Display(string text, string text2) { list.Items.Add(String.Format("{0} {1}", text, text2)); } void OrderBookNodes_AfterInsert(object sender, Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventArgs e) { Microsoft.Office.Interop.Word.XMLNode node = sender as Microsoft.Office.Interop.Word.XMLNode; Display("AfterInsert", node.BaseName); } void OrderBookNodes_BeforeDelete(object sender, Microsoft.Office.Tools.Word.NodeInsertAndDeleteEventArgs e) { Microsoft.Office.Interop.Word.XMLNode node = sender as Microsoft.Office.Interop.Word.XMLNode; Display("BeforeDelete", node.BaseName); } void OrderBookNodes_ContextEnter(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("ContextEnter", e.NewXMLNode.BaseName); } void OrderBookNodes_ContextLeave(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("ContextLeave", e.NewXMLNode.BaseName); } void OrderBookNodes_Select(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("Select", e.Selection.Text); } void OrderBookNodes_Deselect(object sender, Microsoft.Office.Tools.Word.ContextChangeEventArgs e) { Display("Deselect", e.Selection.Text); } void OrderBookNodes_ValidationError(object sender, EventArgs e) { Display("ValidationError", ""); } } }
Loading XML Programmatically with LoadXml
Another addition that VSTO makes to XMLNode is the LoadXml method. The LoadXml method can be used to set the XML on the entire node tree of the XMLNode on which it is called. The LoadXml method has three overloads that take a string of XML, an XmlElement, or an XmlDocument.
LoadXml has one major limitation. It will not decrease or increase the number of XML elements in the document. So given the code in Listing 22-7 that has three book elements and given a document that has only one book in the table mapped to book elements, LoadXml will only transfer the first book to the document. To transfer the second and third book would require the addition of elements, which LoadXml does not do. As a second example, if you have a document that has three books in the table and you call LoadXml passing XML with only one book, LoadXml will update the first row of the table but will leave the two extra books there. The second and third books are left there because LoadXml does not remove elements.
Listing 22-7. The LoadXml Method on XMLNode Object
private void ThisDocument_Startup(object sender, EventArgs e) { this.OrderNode.LoadXml(@" Lah Lah2005-03-19Windows Forms Programming in C#0-321-11620-8Addison- Wesley49.99Effective C#0-321-24566-0Addison- Wesley39.99The C# Programming Language0-321-15491-6Addison-Wesley29.99119.9710.7973 130.7673"); }
You will frequently want to get the XML from an XMLNode or XMLNodes. The way you do this is by using the get_XML method on the Range object returned by the Range property of an XMLNode or XMLNodes. The get_XML method takes a bool parameter to which you pass TRue to get the XML data. If you pass false, you will get the WordML for the XMLNode or XMLNodes instead. Listing 22-8 shows a simple VSTO application that displays the XML data in the document on startup using the root XMLNode called OrderNode.
Listing 22-8. Using the get_XML Method
private void ThisDocument_Startup(object sender, EventArgs e) { MessageBox.Show(this.OrderNode.Range.get_XML(true)); }