XML Notepad Design#
By Chris Lovett, Microsoft
In the GitHub repo, you'll find the core XmlNotepad
DLL project, an Application
project that builds XmlNotepad.exe,
and some setup projects like XmlNotepadSetup
which builds the .msi installer, and the UnitTests
project for testing
XML Notepad.
The following DGML diagram shows the relationships between the main UI classes. The main form contains all the UI elements from the TaskList to the main XmlTreeView with Resizers in between; it's the launching point for the dialogs such as FormSearch, FormSchemas, FormOption, and FormAbout.
The main tab control contains the XmlTreeView and the XsltViewer for showing XSL output. The XmlTreeView contains a TreeView on the left and a NodeTextView on the right and coordinates scrolling between these two views. Both the TreeView and NodeTextView provide the same IntelliSense editing functionality using a TextEditorOverlay component. The XmlTreeView implements IFindTarget, which is used by the Find dialog (FormSearch) to implement find/replace functionality.
The Find dialog supports full text, regex, or XPath expressions and can filter by names or values.
Model#
The core data model behind the UI is System.Xml.XmlDocument
and its XmlNode
objects. These nodes are wrapped by
TreeNode
UI objects. To facilitate support for the XML Include spec, there is a custom reader that processes XML
Includes, and there is also a custom DomLoader that keeps track of line information for error messages.
Validation, IntelliSense, and Custom Editors#
The biggest new feature is IntelliSense, which is driven by XML Schema information provided via the SchemaCache. For example, if your element or attribute is defined by an XSD simpleType and this simpleType contains a list of enumeration facets, then you will get a drop-down like this:
The way this works is that the Checker runs after each edit operation to validate the document and report errors in the
TaskList. This process also puts System.Xml.Schema.XmlSchemaType
information on each element and attribute in the
XmlDocument; then, when editing the value of that node, the TextEditorOverlay uses the XmlIntelliSenseProvider
to get
back a list of possible values. In the above example, it returns the values from the simpleType enumeration facets. For
element name IntelliSense in the tree view, the XmlIntelliSenseProvider
invokes the Checker again, captures
GetExpectedParticles and GetExpectedAttributes on the System.Xml.Schema.XmlSchemaValidator
, and uses that to provide
IntelliSense.
The TextEditorOverlay
also supports custom editors like the DateTimeEditor
or the UriBuilder
or ColorBuilder
.
There are two types of custom editors: IXmlEditors, which are inline editors that replace the default TextBox, and
IXmlBuilders, which are popup dialogs like the OpenFileDialog or ColorDialog. The type of editor is derived from the
schema type information — "xs:date", "xs:time", "xs:datetime" results in the DateTimeEditor, and "xs:anyURI" results in
the UriBuilder. You can also annotate the schema with a custom "vs:builder" attribute in the
http://schemas.microsoft.com/Visual-Studio-IntelliSense
namespace. See the Help content for more information.
Infinite Undo/Redo#
To implement undo/redo, XML Notepad follows a common design pattern of Command objects with Undo and Redo methods.
Commands operate on both a TreeNode
and an XmlNode
because some commands like InsertNode
don't have an XmlNode
yet until they are performed, but the command needs to know where in the tree this new node will be inserted and during
Undo
where it should be removed. The UndoManager collects these in a list. Then the state of the UndoManager controls
the enabled/disabled state of the Undo/Redo MenuItems. When the user selects the Undo menu item, the Undo method is
called on the active command, and that command is pushed onto the Redo stack.
Some operations in the editor cause many edits in the tree, including the replace-all operation and editing the value of
a namespace attribute. (When you change the value of a namespace attribute, every XmlNode bound to that namespace needs
to be reconstructed with a new namespace URI, which can obviously affect a lot of nodes in the tree!) So, to make these
operations one atomic undo operation, there is a CompoundCommand
object that contains a list of smaller edit commands,
and this CompoundCommand is put into the UndoManager
.
Other simpler command objects include the following, which all operate on XmlTreeNode and XmlNode objects:
The PasteCommand is special because it takes random XML text off the clipboard and parses it in the context of the currently selected element in the tree, inheriting the namespaces in scope. The helper class TreeData uses the special XmlTextReader constructor that takes an XmlParserContext as input.
Accessibility#
In order to make testing possible using System.Windows.Automation
, there are some custom AccessibleObject
implementations inside the TreeView and NodeTextView. See AccessibleNode
and AccessibleNodeTextViewNode
. These
accessibility classes should also make Windows accessibility features work better with those custom views.
Missing Documentation?#
If you want more detailed documentation on some aspect of XML Notepad, please create a new GitHub issue.