Skip to content

Implementing a Document and Its Client

Gary edited this page Mar 10, 2015 · 2 revisions

Table of Contents

This section reviews how a document and its client are implemented, as demonstrated in the ATF Simple DOM Editor Sample.

Implementing IDocument

The SimpleDOMEditor sample implements IDocument in its EventSequenceDocument class by deriving from DomDocument, which implements IDocument.

Here are the properties:

public virtual bool IsReadOnly
{
    get { return false; }
}
...
public virtual bool Dirty
{
    get
    {
        return m_dirty;
    }
    set
    {
        if (value != m_dirty)
        {
            m_dirty = value;

            OnDirtyChanged(EventArgs.Empty);
        }
    }
}

And here is the event handler that raises the event:

protected virtual void OnDirtyChanged(EventArgs e)
{
    DirtyChanged.Raise(this, e);
}

Implementing a Document Client

The document client handles the details of how file contents are retrieved and turned into a document, and vice versa. The items in this interface, discussed in IDocumentClient Interface are implemented in the Editor class of the SimpleDOMEditor sample.

DocumentClientInfo Property

First, the implementation of the DocumentClientInfo property is straightforward:

public DocumentClientInfo Info
{
    get { return DocumentClientInfo; }
}
...
public static DocumentClientInfo DocumentClientInfo = new DocumentClientInfo(
    Localizer.Localize("Event Sequence"),
    new string[] { ".xml", ".esq" },
    Sce.Atf.Resources.DocumentImage,
    Sce.Atf.Resources.FolderImage,
    true);

This indicates the application can open files with extensions of ".xml" and ".esq", and multiple documents can be open at once. Icons are also provided for the UI.

CanOpen Method

The CanOpen() method uses DocumentClientInfo.IsCompatibleUri() to determine if the file's extension is one of the ones set up in the DocumentClientInfo:

public bool CanOpen(Uri uri)
{
    return DocumentClientInfo.IsCompatibleUri(uri);
}

Open Method

SimpleDOMEditor uses the ATF DOM for its application data. The ATF DOM supports saving its data to XML using the DomXmlWriter class and reading it with DomXmlReader, and SimpleDOMEditor takes full advantage of this.

The Open() method uses DomXmlReader if the file exists, and otherwise creates a new document. The DomNode, node, is the root node of the DomNode tree.

Through adaptation, a DomNode can be dynamically cast to many different kinds of objects. In this method, the root DomNode is cast to both an EventSequenceDocument and an EventSequenceContext. This can be done, because both EventSequenceDocument and EventSequenceContext are DOM node adapters: both ultimately derive from the DomNodeAdapter class. For more information about adaptation, see Adaptation in ATF and the ATF Programmer’s Guide: Document Object Model (DOM), downloadable at ATF Documentation.

The second part of this method creates an EventSequenceDocument, document, which derives from DomDocument. The method casts the root DomNode node as an EventSequenceDocument object. It goes on to set various properties for the EventSequenceDocument object.

The document is displayed in a ListView control, which is part of the EventSequenceContext that is also cast from the root DomNode. The method sets up a ControlInfo for this control and also registers it.

This shows that the object implementing the IDocument interface can be adapted to various classes, as needed.

public IDocument Open(Uri uri)
{
    DomNode node = null;
    string filePath = uri.LocalPath;
    string fileName = Path.GetFileName(filePath);

    if (File.Exists(filePath))
    {
        // read existing document using standard XML reader
        using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            DomXmlReader reader = new DomXmlReader(m_schemaLoader);
            node = reader.Read(stream, uri);
        }
    }
    else
    {
        // create new document by creating a Dom node of the root type defined by the schema
        node = new DomNode(Schema.eventSequenceType.Type, Schema.eventSequenceRootElement);
    }

    EventSequenceDocument document = null;
    if (node != null)
    {
        // Initialize Dom extensions now that the data is complete
        node.InitializeExtensions();

        EventSequenceContext context = node.As<EventSequenceContext>();

        ControlInfo controlInfo = new ControlInfo(fileName, filePath, StandardControlGroup.Center);
        context.ControlInfo = controlInfo;

        // set document URI
        document = node.As<EventSequenceDocument>();
        document.Uri = uri;

    // set document GUIs for search and replace
    document.SearchUI = new DomNodeSearchToolStrip();
    document.ReplaceUI = new DomNodeReplaceToolStrip();
    document.ResultsUI = new DomNodeSearchResultsListView(m_contextRegistry);

    context.ListView.Tag = document;

        // show the ListView control
        m_controlHostService.RegisterControl(context.ListView, controlInfo, this);
    }

    return document;
}

Show Method

Show() uses an EventSequenceContext object, which it also casts from the EventSequenceDocument, document. The method then uses the ControlHostService object to show the document, which is displayed in a ListView in the context:

public void Show(IDocument document)
{
    EventSequenceContext context = Adapters.As<EventSequenceContext>(document);
    m_controlHostService.Show(context.ListView);
}

Save Method

To complete the symmetry of the Open() method, Save() uses the DomXmlWriter class to write the document to an XML file. The DomXmlWriter.Write() method needs a DomNode, so the document is adapted to one:

public void Save(IDocument document, Uri uri)
{
    string filePath = uri.LocalPath;
    FileMode fileMode = File.Exists(filePath) ? FileMode.Truncate : FileMode.OpenOrCreate;
    using (FileStream stream = new FileStream(filePath, fileMode))
    {
        DomXmlWriter writer = new DomXmlWriter(m_schemaLoader.TypeCollection);
        EventSequenceDocument eventSequenceDocument = (EventSequenceDocument)document;
        writer.Write(eventSequenceDocument.DomNode, stream, uri);
    }
}

Close Method

To close this DocumentClientInfo interface implementation discussion, Close() again adapts the document to an EventSequenceContext. It gets the ListView control from the context and unregisters it using the ControlHostService object. It performs various context clean up. Finally, it uses the DocumentRegistry object to invoke the Remove() method to remove the document from the document registry.

public void Close(IDocument document)
{
    EventSequenceContext context = Adapters.As<EventSequenceContext>(document);
    m_controlHostService.UnregisterControl(context.ListView);
    context.ControlInfo = null;

    // close all active EditingContexts in the document
    foreach (DomNode node in context.DomNode.Subtree)
        foreach (EditingContext editingContext in node.AsAll<EditingContext>())
            m_contextRegistry.RemoveContext(editingContext);

    // close the document
    m_documentRegistry.Remove(document);
}

Topics in this section

Clone this wiki locally