Skip to content

DOM Adapters

Gary edited this page Mar 10, 2015 · 2 revisions

Table of Contents

DOM adapters derive from DomNodeAdapter and allow you to adapt DomNodes of a given type to other types of objects so you can easily manipulate the underlying application data and also monitor DomNode changes for validation and synchronization.

DOM extensions are classes that "extend" a DomNode's capabilities. Extensions have two basic functions: adapting a DomNode's data type to another type, and listening to events on nodes and their children.

With DOM extensions, you don't need to handle the data or even the DomNode tree directly. Instead, you implement DOM extensions and define them for types in the data model. The application then accesses data in the DomNode tree through these DOM extensions and processes it.

DOM extensions are nearly always derived from the DomNodeAdapter class, and such extensions are called DOM adapters. The DomNodeAdapter class provides a useful infrastructure for DOM adapters, and there is no reason to create a DOM extension that is not a DOM adapter. DOM adapter is the term used here, because all DOM extensions are DOM adapters, practically speaking, and that's the case in the ATF samples. The term adapter is often used to refer to a DOM adapter, for short.

In a DOM adapter, you can:

  • Provide an API for data in the DomNode, such as wrapping attributes in properties. You treat the DomNode as an instance of the DOM adapter class.
  • Provide initialization behavior when the adapter is created or when it is initialized for its underlying DomNode.
  • Manage changes to data in the DomNode tree, both for this type and related types, including child node types, by listening to events on DOM nodes and their children.
DOM adapters are the most powerful feature of the DOM.

DomNodeAdapter Class

The DomNodeAdapter class provides an infrastructure for DOM adapters, as well as many useful methods and properties for adapters. In ATF, many classes derive from DomNodeAdapter, including the general purpose editing context Sce.Atf.Dom.EditingContext. (For more information on this widely used context, see General Purpose EditingContext Class.)

The DomNodeAdapter base class includes these features:

  • Implementation of the IAdapter, IAdaptable, and IDecorator interfaces to provide adaptation for the DOM node. For more information on these interfaces, see General Adaptation Interfaces under Adaptation in ATF.
  • DomNode property to access the adapter's underlying DOM node.
  • OnNodeSet() method, which is invoked when the adapter is initialized for the node. The adapter must be initialized before using it. OnNodeSet() is called only once per node for each adapter. Override this method in the DOM adapter class. For a discussion of what this method can do, see OnNodeSet Method.
  • Methods to test for the existence of a DOM adapter and for getting a DOM adapter. For information on these methods, see Adaptation methods.
  • Utility methods to manipulate child and parent data of the DOM node, to get and set attributes of the node, and to manage references between DOM nodes. For more information, see Other Important Methods.

Adaptation methods

These methods perform adaptation for a DomNode:

  • Is<T>(): Determine whether a DOM adapter is available for T.
  • As<T>(): Return a DOM adapter for T; return null if no adapter is available.
  • Cast<T>(): Return a DOM adapter for T; raise an exception if no adapter is available.
These are all extension methods in the Sce.Atf.Adaptation.Adapters class. For information on how these methods are used in the DOM, see Initializing DOM Adapters and DOM Adapters Adapt in this section. For details on what these methods do and how they work, see General Adaptation Classes in the Adaptation in ATF section.

OnNodeSet Method

When a DOM adapter is initialized, the DomNodeAdapter.OnNodeSet() method is called. For information on how to initialize them, see Initializing DOM Adapters.

Use OnNodeSet() to perform initialization or synchronization tasks you want performed whenever a DOM adapter is initialized for a DomNode. OnNodeSet() is called once: the first time an adapter is initialized for a node. The Is<T>(), As<T>(), and Cast<T>() methods all call OnNodeSet().

When overriding OnNodeSet(), you should always call base.OnNodeSet() to ensure that any code in the base class DomNodeAdapter is invoked.

Common tasks for OnNodeSet() include:

  • Updating any data in the adapter and the node that can get out of sync, for example, positions, bounding boxes, or internal state properties (hidden, locked, and so on).
  • Calling As<T>() to initialize the DomNode for some other adapter and then assign the returned adapter to a field. Multiple DOM adapters defined on the same type frequently operate in conjunction with each other on the same DOM data.
  • Subscribing to events to track changes to data in the DomNode.
For example, this OnNodeSet() method is from the Fsm class in the ATF Fsm Editor Sample. The Fsm class is a DOM adapter that adapts DomNodes of the root type "fsmType". The Fsm class contains lists of State, Transition, and Annotation (text comment) objects in a state machine. Its OnNodeSet() method uses the DomNodeListAdapter class to get a list of DOM adapters for the various types of children of a node:
protected override void OnNodeSet()
{
    m_states = new DomNodeListAdapter<State>(DomNode,
        Schema.fsmType.stateChild);
    m_transitions = new DomNodeListAdapter<Transition>(DomNode,
        Schema.fsmType.transitionChild);
    m_annotations = new DomNodeListAdapter<Annotation>(DomNode,
        Schema.fsmType.annotationChild);
}

DomNodeListAdapter<T>() adapts a node child list to a list of T items, where T is typically a DOM adapter class that is defined on the type of the child nodes, as in the example above.

Other Important Methods

DomNodeAdapter provides methods that are widely used in DOM adapters, especially GetAttribute<T>() and SetAttribute():

  • T GetAttribute<T>(AttributeInfo attributeInfo): Get the value of the attribute, cast to type T.
  • void SetAttribute(AttributeInfo attributeInfo, object value): Set the value of the adapted DomNode's attribute.
  • T GetChild<T>(ChildInfo childInfo): Get the child described by the given ChildInfo metadata, cast to type T.
  • void SetChild(ChildInfo childInfo, IAdaptable value): Set the child of an adapted DomNode to a DomNode.
  • IList<T> GetChildList<T>(ChildInfo childInfo): Get the children of an adapted DomNode.

Defining DOM Adapters for Types

A DOM adapter is specified by defining an adapter for a type of DomNode, that is, for a DomNodeType.

Once you have defined a DOM adapter for a type, any {''}DomNode{''} of this type can be adapted to the class of the DOM adapter.

You can define as many adapters as you want for each type. After this, a {''}DomNode{''} of that type can be adapted to any interface that any of the DOM adapters defined on that type have implemented. For details, see Adapting to All Available Interfaces.

Typically, DOM adapters are defined in the schema loader's OnSchemaSetLoaded() method.

You use the DomNodeType.Define() method to define a DOM adapter for a type:

public void Define(ExtensionInfo extensionInfo)

As mentioned previously, ExtensionInfo derives from FieldMetadata and is used to represent a DOM adapter.

For example, here is the ATF Using Dom Sample's OnSchemaSetLoaded() method:

protected override void OnSchemaSetLoaded(XmlSchemaSet schemaSet)
{
    foreach (XmlSchemaTypeCollection typeCollection in GetTypeCollections())
    {
        m_namespace = typeCollection.TargetNamespace;
        m_typeCollection = typeCollection;
        GameSchema.Initialize(typeCollection);

        // register extensions
        GameSchema.gameType.Type.Define(new ExtensionInfo<Game>());
        GameSchema.gameType.Type.Define(new ExtensionInfo<ReferenceValidator>());
        GameSchema.gameType.Type.Define(new ExtensionInfo<UniqueIdValidator>());

        GameSchema.gameObjectType.Type.Define(new ExtensionInfo<GameObject>());
        GameSchema.dwarfType.Type.Define(new ExtensionInfo<Dwarf>());
        GameSchema.ogreType.Type.Define(new ExtensionInfo<Ogre>());
        break;
    }
}

GameSchema is the sample's schema class defining the various metadata classes for the types, described in Using the DomGen Utility to Create a Schema Class. GameSchema.gameType.Type is the DomNodeType for "gameType", and several DOM adapters are defined for this type. Once these DOM adapters have been defined, they can be used to adapt DOM nodes and also monitor events on the nodes.

In particular, you can define adapters for the type of the DOM tree's root DomNode, which allows you to listen to events on all DomNodes. Such a DOM adapter class could be a viewing context, an editing context, a document, or a validator for DOM data. The ATF samples show many examples of this, including the above example where DOM adapters are defined on the root element's type "gameType".

When you define an adapter for a type, the adapter is also defined for any types based on that type in the XML Schema. For instance, the UsingDom sample's XML Schema defines "ogreType" as based on "gameObjectType":

<xs:complexType name="ogreType">
  <xs:complexContent>
    <xs:extension base="gameObjectType">
      <xs:attribute name="size" type="xs:integer"/>
      <xs:attribute name="strength" type="xs:integer"/>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

As seen above, the GameObject DOM adapter is defined on "gameObjectType", so GameObject is also defined for "ogreType".

The UsingDom sample is useful for understanding DOM adapters and DOM concepts in general. For more details on this sample, see Using Dom Programming Discussion.

Initializing DOM Adapters

DOM adapters actually operate on individual DomNodes or subtrees of DomNodes. You define a DOM adapter for a DomNodeType, but you initialize a DOM adapter for a specific DomNode of that DomNodeType. You must initialize the DOM adapter before it is used.

For a given DomNode, you initialize a DOM adapter defined for that DomNode's DomNodeType. You can initialize a single DomNode with one of the casting functions As<T>() and Cast<T>() and then use the resulting value as if it were an object of the adapter's type. Calling the method Is<T>() also initializes the DOM adapter for the node it is invoked on.

For example, Defining DOM Adapters for Types previously showed defining the DOM adapter Game on the type "gameType". This code creates a new DomNode of "gameType" and then gets an instance of the Game DOM adapter for that node:

DomNode root = new DomNode(GameSchema.gameType.Type, GameSchema.gameRootElement);
Game game = root.As<Game>();

The DOM adapter Game is now initialized for the DomNode root, and the variable game can be used as if it were a Game object, even though it is actually the root DomNode.

DOM adapters are also used to monitor events on DomNodes. The DomNode does not need to be cast to initialize a DOM adapter, because in this case, an object of the DOM adapter's type is not used to monitor events. In such a case, you can initialize a whole subtree of DOM nodes with the DomNode.InitializeExtensions() method, which initializes all the DOM adapters defined for that DomNode's type.

Several samples have a line like this, where node is the root DomNode:

// Initialize Dom extensions now that the data is complete
node.InitializeExtensions();

This initializes all DOM adapters that are defined for the type of the root DomNode. This allows listening to all DomNodes in the tree.

DOM Adapters Adapt

Adaptation is the process of allowing an object of one type to behave like an object of another type. You can think of adaptation as dynamic casting. For general information about adaptation, see the Adaptation in ATF section.

When you adapt a DomNode to a DOM adapter, the DomNode is dynamically cast to the DOM adapter class's type. As a result, all the adapter class's methods, properties, and events are available to manipulate the data represented by the DOM node, just as if the DOM node were an instance of that class. In other words, adapters provide an API for an application to manipulate DOM data. You can design the adapter with an API that is convenient for you to work with and makes it easier to change the data in response to user editing actions.

Either the As<T>() or Cast<T>() method, mentioned in Adaptation methods, is called to get the adapted DomNode. This also initializes the DOM adapter, as previously mentioned. This code creates a new DomNode for a prototype and then gets the Prototype DOM adapter for that node:

DomNode node = new DomNode(Schema.prototypeType.Type);
Prototype prototype = node.As<Prototype>();

Often nodes are created and their corresponding adapters retrieved at the same time, as in this line adapting a DomNode to the Pin DOM adapter:

Pin pin = new DomNode(Schema.pinType.Type).As<Pin>();

You can now use all the Pin class's methods, properties and events with pin, even though pin is actually a DomNode object, not a Pin object.

Wrapping Attributes and Elements in Properties

One of the advantages of using a DOM adapter for adaptation is that a type's attributes and other data can easily be mapped to C# properties. Properties enable you to provide an object model for attributes and data in the underlying DomNode.

The ATF Using Dom Sample demonstrates creating DOM adapters that have properties corresponding to the data model types' attributes. The "gameObjectType" type has just one attribute, "name". This sample defines the DOM adapter GameObject for the "gameObjectType" type, which has one property, Name:

public class GameObject : DomNodeAdapter
{
    /// <summary>
    /// Gets or sets game object name</summary>
    public string Name
    {
        get { return GetAttribute<string>(GameSchema.gameObjectType.nameAttribute); }
        set { SetAttribute(GameSchema.gameObjectType.nameAttribute, value); }
    }
}

GameObject's Name property uses the DomNodeAdapter methods GetAttribute<T>() and SetAttribute() on nameAttribute for the property's getter and setter. For further details on this sample's DOM adapters, see DOM Adapters in UsingDom.

DOM Adapters Listen

Besides adaptation, DOM adapters can listen for events on DomNodes. The DomNode class has a set of events, noted in DomNode Class.

DOM adapters that listen to DomNode events can perform a variety of operations, such as validating data as it changes. Such adapters subscribe to events of interest and perform their processing in the event handlers. For instance, validator adapters can listen to AttributeChanging events and check that new attribute values are valid. For a discussion of DOM validation, see DOM Adapters Validate.

This technique also allows adapters to keep the DOM tree structure fully in sync with the underlying data on which adapters operate. As the user changes data in the application's user interface, the DOM adapter can make the corresponding changes to DOM data through the DOM adapter.

It is useful to define listening DOM adapters on the type of the root DOM node. This allows the DOM adapter to listen for events on all DomNodes.

DOM Adapters Validate

ATF provides additional support for DOM adapters that listen to events, so they can perform automatic validation of DOM data as it changes. Such adapters are known as DOM validators.

DOM validators listen to node events and are typically defined on the type of the DomNode tree's root, so they can check all nodes in the tree.

Some validators are automatically provided if you specify constraints in an XML Schema. There are also some standard ATF validators that make common checks, such as for unique ID attributes. You can also customize validation by either creating your own validation rules or your own DOM validators.

IValidationContext and Validation

DOM validation must take place inside the validation context IValidationContext, which is an interface for contexts that perform validation. In this context, validations occur within a transaction. When validations occur within transactions and the change is invalid, the change can be rolled back. The Sce.Atf.Dom.EditingContext class can provide such a context, because IValidationContext is implemented by a class EditingContext derives from. IValidationContext must be implemented by at least one DOM adapter defined for a type that a validator is defined for, so it is available to all the DOM adapters defined for that type. For a further explanation, see Implementing Interfaces for DOM Adapters.

Built in Validators

If you use XML schema for your data definition language, some validation rules are created automatically when your schema is loaded by XmlSchemaTypeLoader, such as checking the range of a DOM node attribute's value. To use such validators, define the appropriate restrictions in your XML Schema and define the DataValidator DOM adapter for your root DomNode's type.

For example, this XML Schema defines a simple type "boundedInt" and then uses it as the type for the "duration" attribute of the complex type "eventType":

<xs:simpleType name="boundedInt">
  <xs:restriction base="xs:integer">
    <xs:minInclusive value="1"/>
    <xs:maxInclusive value="100"/>
  </xs:restriction>
</xs:simpleType>
...
<!--Event, with name, start time, duration and a list of resources-->
<xs:complexType name ="eventType">
  <xs:sequence>
    <xs:element name="resource" type="resourceType" maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute name="name" type="xs:string"/>
  <xs:attribute name="time" type="xs:integer"/>
  <xs:attribute name="duration" type="boundedInt"/>
</xs:complexType>

Data validation is enabled by defining the DataValidator DOM adapter for the type of the root element "eventSequenceType":

Schema.eventSequenceType.Type.Define(new ExtensionInfo<DataValidator>());

The value of an event's duration is now restricted to these bounds, with no further effort.

If you use a different type definition language than XML Schema and so you have created your own type loader, you need to convert your data restrictions into DOM validation rules and add them to the attribute and child metadata for your types. For information on adding rule restrictions to these metadata classes, see Using Rule Classes with DataValidator.

ATF DOM Validators

ATF provides a set of DOM validators. For instance, UniqueIdValidator checks that ID attributes are unique across all the DomNodes. Another DOM validator, DataValidator, allows you to use rule classes to validate application data. For example, the classes NumericMinRule and NumericMaxRule are rule classes that check that the value of a type attribute is between a minimum and maximum value. To learn how to define and use rule classes, see Using Rule Classes with DataValidator.

The following table shows the standard validation classes ATF offers. The most commonly used ones are DataValidator, UniqueIdValidator, and ReferenceValidator.

Validator class Purpose Derives from
Validator Abstract base class for DOM validators. Derive from this class to create your own validator. Observer, which derives from DomNodeAdapter
DataValidator Validator for attribute and child rules, such as ones defined in the data model. For more information, see Using Rule Classes with DataValidator. Validator
DependencyValidator Tracks dependencies between DOM nodes. Validator
ReferenceValidator Validates internal references between DOM nodes and references to external resources. Validator
LockingValidator Validates locked data in DOM nodes. Requires an adapter that implements ILockingContext, that is, ILockingContext should be implemented by at least one DOM adapter defined for a type the validator is also defined for. Validator
IdValidator Abstract base class to validate ID attributes. Validator
UniqueIdValidator Ensure that each DOM node has a unique ID. IdValidator
UniquePathIdValidator Ensure that each DOM node has a unique path in the DOM node tree. IdValidator

It's easy to use these validators:

  • Define them for the type of the root DomNode.
  • Initialize them by calling DomNode.InitializeExtensions() on the root DomNode. Because they are DOM adapters, they must be initialized to operate.

Using Rule Classes with DataValidator

The DataValidator class allows you to use rule classes — existing or new — to validate application data. Rule information resides in the type metadata classes discussed in DomNodes and DOM Metadata Classes. DataValidator performs validation by calling the Validate() method of the AttributeInfo and ChildInfo metadata classes.

ATF Rule Classes

This table describes the rule classes ATF provides. If you use an XML Schema for your type definition, these rules are automatically created from any XML Schema restrictions and added to the type metadata classes' objects when XmlSchemaTypeLoader loads the type definition file.

Rule class Purpose Derives from
AttributeRule Abstract base class for validator rules to restrict attributes. Extend this to create restrictions on attributes. None
NumericMinRule Rule for attribute minimum value. AttributeRule
NumericMaxRule Rule for attribute maximum value. AttributeRule
StringEnumRule Rule for attribute enumeration of string values. AttributeRule
ChildRule Abstract base class for validator rules describing child restrictions. Extend this to create child restraints. None
ChildCountRule Rule for number of children. ChildRule

Creating and Using New Rule Classes

Validation rules are simple classes, and it is easy to create your own if you use your own data model or type definition language. Simply extend the AttributeRule or ChildRule base class and implement the new class's Validate() method to create your own data validation rule. For examples, see the existing rule classes listed above.

Follow these steps to create and add a new rule:

  1. Create a new rule class by extending the AttributeRule or ChildRule base class. Put the restrictions you want in this new rule class's Validate() method.
  2. Call the AddRule() method with the new rule on the AttributeInfo or ChildInfo instance to add the rule. The schema loader is a good place to do this.
  3. Define DataValidator on a type, nearly always the type of the root DomNode, which results in Validate() being called on AttributeInfo and ChildInfo instances.
  4. Initialize the DataValidator DOM adapter for DomNodes by calling the DomNode.InitializeExtensions() method on a DomNode of the type you defined DataValidator on. This is usually the root DomNode's type.
Here is an example of code that could be added to the ATF Fsm Editor Sample's SchemaLoader.OnSchemaSetLoaded() method to restrict the size of a "stateType" object:
Schema.fsmType.Type.Define(new ExtensionInfo<DataValidator>());
Schema.stateType.sizeAttribute.AddRule(new NumericMaxRule(100, true));

The first line defines a DataValidator DOM adapter on "fsmType", which is the type of the root DOM node in the data model, so the validator can listen to change events in all nodes, including nodes of "stateType". In addition, DataValidator requires that validation takes place inside the validation context IValidationContext, and "fsmType" has another DOM adapter defined that implements IValidationContext, so this requirement is met. The second line adds a rule to the AttributeInfo Schema.stateType.sizeAttribute, which represents the size of a "stateType" object in the Fsm Editor. The rule class NumericMaxRule restricts the maximum value of an attribute, to less than or equal to 100, in this case. If you are running the Fsm editor and attempt to enter a size greater than 100 for a selected state object, an error message displays, warning you an invalid attribute value was entered. A new rule class could also have been defined that placed more complex restrictions on size values.

To learn more, examine the Validate() method in some of the rule classes, such as NumericMinRule. Also, look at the AddRule() method in the AttributeInfo and ChildInfo classes.

Implementing Custom Validators

You can also define custom DOM validator classes on your data types to check data automatically as it changes.

If existing ATF validator classes do not suit your needs or creating rules would not work, create your own validator classes by extending the abstract base class Validator or a class that already extends Validator. Validator includes a set of common transaction methods that allow you to watch specific events in the DOM and, when validation transactions occur, to check the DOM data as it changes.

Validator extends Observer, which is a DOM adapter that monitors changes to the DomNode tree by subscribing to DomNode events. Observer provides a set of methods that can be overridden to perform special processing in response to DomNode tree changes.

Recall that DOM validation must take place inside the validation context IValidationContext. For information on how to do this, see IValidationContext and Validation. Validator overrides some Observer methods to provide an IValidationContext. For details on using validators within an IValidationContext, see Implementing Interfaces for DOM Adapters.

Override the methods for events you want to follow in the Validator class or a class that extends Validator. This table summarizes the available methods in Validator and the methods' parameters.

Method Purpose Arguments
AddNode() Perform custom actions for a DomNode (a new node or a child node) added to the tree.
  • Added DomNode
RemoveNode() Perform custom actions for any node removed from the tree.
  • Removed DomNode
OnAttributeChanged() Called when an attribute in a DomNode in the tree has changed. Overriding this method is the same as listening to the DomNode.AttributeChanged event.
  • DomNode at root of tree
  • AttributeEventArgs
OnBeginning() Called at the start of a validation transaction.
  • IValidationContext object
  • System.EventArgs
OnCancelled() Called if the validation was cancelled.
  • IValidationContext object
  • System.EventArgs
OnEnding() Called just before the validation transaction ends.
  • IValidationContext object
  • System.EventArgs
OnEnded() Called after validation has ended.
  • IValidationContext object
  • System.EventArgs

These methods use the AttributeEventArgs and System.EventArgs classes to provide event information, such as the changed DomNode and AttributeInfo. For more about DomNode events, see DomNode Class.

The Validator class includes the Validating property, which is true when a validation context is active, indicating a validation is currently in progress. Validating is true after OnBeginning() is called and false after OnCancelled() or OnEnding() is called. Use the Validating property in the DOM event methods to ensure that a validation transaction is taking place, as in this example:

protected virtual void OnAttributeChanged(object sender,
    AttributeEventArgs e)
{
    if (Validating)
    {
        m_locked = m_lockingContext.IsLocked(e.DomNode);
    }
}

If data the validator is testing fails to meet your criteria during a transaction, throw the InvalidTransactionException exception. This ensures that the transaction can be rolled back and all the original DOM data preserved.

In this next example, the ATF Timeline Editor Sample uses a validator to ensure that:

  • Timeline event start times are greater than or equal to 0 and snap to the nearest integer.
  • Interval lengths are greater than or equal to 1 and snap to the nearest integer.
Both tests are made in the OnAttributeChanged() method in the TimelineValidator class, which derives from Validator. Only the test for the timeline event start time is shown here:
protected override void OnAttributeChanged(object sender,
    AttributeEventArgs e)
{
    BaseEvent _event = e.DomNode.As<BaseEvent>();
    if (_event != null && Validating)
    {
        if (e.AttributeInfo.Equivalent(Schema.eventType.startAttribute))
        {
            float value = (float)e.NewValue;
            float constrained = Math.Max(value, 0);                 // >= 0
            constrained = (float)MathUtil.Snap(constrained, 1.0);   // snapped to nearest integral frame number
            if (constrained != value)
                throw new InvalidTransactionException(Localizer.Localize(
                    "Timeline events must have a positive integer start time"));
            return;
        }

        Interval interval = _event.As<Interval>();
        if (interval != null)
        {
            // further validation for intervals
            ...
        }
    }
}

If the conditions are not met, the InvalidTransactionException exception is thrown.

Custom validators are demonstrated in the ATF Circuit Editor Sample, the ATF DOM Tree Editor Sample, the ATF Fsm Editor Sample, and the ATF Timeline Editor Sample. For ideas on writing your own validators, see the code in these samples. For instance, TransitionRouter is a custom validator class (deriving from Validator) in the Fsm Editor that tracks changes to transitions between states and updates their routing during validation.

Implementing Interfaces for DOM Adapters

Some DOM adapters require certain interfaces to be implemented to operate properly.

For example, the DataValidator DOM adapter needs IValidationContext. This need is satisfied if the type that DataValidator is defined on has another DOM adapter that implements IValidationContext defined on that type. The validator and DOM adapter implementing IValidationContext must also be initialized for the same DomNode, which can be done by calling DomNode.InitializeExtensions() on this node. The net result is that the adapted DomNode can also be adapted to IValidationContext.

For an explanation of why an interface implemented by a DOM adapter defined on a type is available to all DOM adapters defined on that type, see Adapting to All Available Interfaces.

Normally, the root DomNode is adapted to validators and to DOM adapters providing context interfaces. This way, DOM adapters can use any interface implemented by any DOM adapter initialized for the root DomNode. In addition, DOM adapters initialized on the root DomNode can listen to events on all DomNodes in the tree.

Topics in this section

Clone this wiki locally