MForm navigation - Description
Testing the MForm navigation with the Navigation Toolbox

The MForm Toolbox that is placed on pages with MForm available has a special form called "Navigation Toolbox".

The navigation toolbox allows providing a xpath expression and an action that should be performed on the found controls. This is an easy way to learn MForm XPath subset.

You will notice that the Navigation toolbox is also a MForm control. To see how it is implemented, see ~/Toolbox/MFormToolbox.ascx user control.

In order to enable the Navigation toolbox, the checkbox below must be enabled.

Navigating through MForm controls

Because MForm controls can be repeatedly generated using the MinOccurs and MaxOccurs properties, it is hard to relate to the inner MForm controls programmatically using only their IDs.

To resolve this problem, a language for navigating through the controls has been prepared. This language is a subset of the XML Path Language (XPath), taking advantage of the fact that MForm controls correspond to XML nodes.

Example 1

Using this language, it is very easy to find for example all MForm controls, whose local name equals "SomeControl".

The path would look like this: //*:SomeControl

The location path

The MForm XPath abbreviated syntax uses the location path construct. A location path may consist of one or more location steps.

The navigation starts from one MForm control (usually a root, but can be started from any MForm control), this control is queried with the first location step from the location path resulting in an enumeration of MForm controls. All of the MForm controls in this enumeration are then queried with the next location step and so on until the last location step, which returns the output result of the whole location path.

Example 2

Assume that we start the navigation from an MForm control that has a child MForm control called "Parent". This control has a child MForm control called "ItsChild", and subsequently this control has a child MForm control called "TheGrandChild".

The path that directs to the last control would look like this: Parent/ItsChild/TheGrandChild

The single location step

A single location step has three parts:

  • an axis, which specifies the tree relationship between the nodes selected by the location step and the context node,
  • a node test, which specifies the node type and expanded-name of the nodes selected by the location step, and
  • zero or more predicates, which use arbitrary expressions to further refine the set of nodes selected by the location step.
The axes

The MForm XPath abbreviated syntax allows 4 axes:

  • the child axis - /
  • the descendant or self axis - //
  • the self axis - .
  • the parent axis - ..

In case of child axis and descendant or self axis, using the node test is required.
In case of the other two axes (the self axis and the parent axis), using the node test is prohibited.

Example 3

When looking for all MForm controls, that have a "SomeControl" as child, you can use this path:

//SomeControl/..

Example 4

Assume that we are in a MForm control that has a child named "Owner".

The path Owner//. will return the "Owner" control, its children, the children of its children and so on.

The node test

The node test checks the name and type of the control.
The MForm XPath abbreviated syntax allows the following node tests:

  • the element node test - ElementName or prefix:ElementName
  • the attribute node test - @AttributeName
  • the all nodes test - *
  • the all nodes in namespace test - prefix:*
  • the all nodes with local name test - *:LocalName
  • the all attributes test - @*

In real XPath attributes are not treated as children. So a * node test does not return attributes On the contrary, MForm controls are treated as children no matter if they represent elements or attributes, so the * will return both the controls representing elements and attributes.

Also the *:LocalName test will test both attribute and element MForm controls.

Example 5

When looking for all MForm controls representing attributes, whose parent's local name is "AttributeOwner", use the following xpath: use this path:

//*:AttributeOwner/@*

Another difference between real XPath and its MForm implementation is that XML works on real nodes, whereas MForm works on controls that may, but don't have to build a part of ouput XML.

Let's consider the MForm Choice control. The MForm output XML will not use the name of the control anyhow, because only the selected item of the choice control becomes part of the XML (so the standard XPath does not give a way to ask about the choice control). But because there must be a way to locate the choice control in the MForm tree, the choice control is treated by the query engine in the same manner as it treats a control that represents an XML element.

Example 6

Assuming that we have a Choice MForm control named "Choice" located somewhere in the tree and we want to find all its children (all the actual choices).

The proper XPath would be: //Choice/*

The predicates

The predicates in XPath are noted in square brackets - [ ].

The implementation of predicates in MForm XPath is currently rudimentary. The only predicate that MForm supports is the index predicate

If some MForm control has several instances under one parent, they can be disguised using the index predicate. The index of the first control is 1.

Example 7

To find the first instance of the "Child" MForm control, use the following xpath:

Child[1]

The compound location step

MForm XPath implementation provides a possibility to use unions in location steps. The union is noted as a | between single location steps. All location steps constituting the union should be inside round brackets:
(SingleLocationStep | OtherSingleLocationStep | EvenDifferentLocationStep)

The unions may be used only between location steps, not between parts of location paths.

Example 8

To find all attribute controls and the "AlsoMe" element control of the "Parent" control, located under the control that we run the path, use the following xpath:

Parent/(@* | AlsoMe)

Example 9

To find all controls whose parents are called "Mommy" or "Daddy", use the following xpath:

//(Mommy | Daddy)/*

Using MForm XPath navigation

MForm Xpath navigation should be simple and intuitive for all the developers that already know XPath. Besides few exceptions (described in the text above) the Xpath query run on the controls would also work as the XPath query run on the MForm output XML.

The reason for creating the MForm XPath navigation language is actually creating such a simple an intuitive tool for querying MForm controls, not implementing the whole XPath functionality. Probably the MForm Xpath subset will be broadened a bit but only for consistency reasons (i.e. the descendant-or-self axis is implemented but the ancestor-or self axis is not).

MForm XPath and LINQ

It is worth mentioning that an output of the MForm XPath Select extension method is an IEnumerable<OffspringItem> controls, which further can be refined using the LINQ query (this is the most important reason why predicates are barely implemented in MForm, they can simply be done with LINQ).

Example 8

Assume that we have a "sampleRoot" MForm Root control and we want to find all the item controls with a value equal "true" and are the children of the "Flags" MForm control.
This code will not work:

sampleRoot.Select("//Flags/*[. = 'true']")

because value predicate is not implemented with MForm XPath. However, the same thing can be implemented like this:

sampleRoot.Select("//Flags/*").Leafs().Where(leafItem => leafItem.Value == "true")
(Leafs() is an extension method that does exactly the same as OfType<LeafItem>())

Of course the second query is longer, but on the other hand all steps are easier to follow (first take only leaf items, because only them can have values, then check the values) and what is more important, we get the actual control and can check all of its properties (also having intellisense checking, etc.).

Example 9

LINQ and MForm XPath can be combined in a manner where there is more than one XPath query.

Let's assume that we have a "clientsRoot" MForm Root control for providing many clients data. This control may have the following tree structure:

  • Clients
    • Client (1 to many)
      • FirstName
      • Surname
      • Adresses
        • Address (1 to many)
          • Street
          • City

Now we want to find all the "Client" controls, that represent Mr. Jones, whose all addresses are in the city of Jonestown.


clientsRoot.Select("//Client").Where(
    client => 
        client.Select("Surname").Leafs().Where(surname => surname.Value == "Jones")
        &&
        client.Select("Addresses/Address/City").Leafs().All(city => city.Value == "Jonestown")
);