HTML and XAML Find Expressions
FindExpression's are the replacement/evolution of FindParam's. In version 2.0, we will be using FindExpression's as the base for all element searches in the DOM, HWnd, or control trees (including Translator Locators). The key design goal for FindExpressions is to enable a flexible, rich and extensible search definition pattern that can be used across all of our Telerik technologies. This will enable our customers to carry over their product experience as they move across our product stacks making the learning curve for customers moving from one technology to another minimal.
Some of the key problems we faced with FindParam's that are solved by using FindExpression's are the following:
- FindParam's are very closely coupled with HTML. The object model for FindParam's was built with HTML in mind.
- Chained identifications [e.g. find element 'foo' then 'bar' starting at 'foo'] cannot currently be represented as one FindParam object.
- The ability to extend the capability of the current search routines based on FindParam's is expensive. The search logic is disconnected from the search definition.
- The FindParam object model is very verbose which results in:
- Constant OM changes when extending search patterns.
- OM changes will always require serialization changes and any serialization change requires extra consideration for backward compatibility.
- Constructing a FindParam is a bit tricky. We have 7 constructors and even these 7 don't satisfy all scenarios. We want our search code to be as concise as possible.
- Complex OMs make Code Gen more complex.
- No extensible infrastructure where customers can build their own search engines if they wish.
- Can't easily be leveraged for searches within other technologies.
The key to understanding FindExpression's is to understand its basic component: the FindClause. A FindClause is a name/value pair with an optional comparison operator. A FindExpression consists of 1-n FindClauses. For example:
=> FindClauses Without Operators.
==> FindClauses With Operators
name=~bar [name attribute partially contains bar]
automationid=^hat [the automation id of an element starts with hat]
[The optional operator is ALWAYS the first character after the '=' in the expression. This special character can be escaped with a preceding '\' if it is meant to be interpreted as a literal character]
Supported Operators in version 2.0
public const char Contains = '~'; // foo=~bar : foo 'contains' bar
public const char NotContain = '!'; // foo=!bar : foo 'does not contain' bar
public const char StartsWith = '^'; // foo=^bar : foo 'starts with' bar
public const char EndsWith = '?'; // foo=?bar : foo 'ends with' bar
public const char RegEx = '#'; // foo=#ba* : foo 'matches regex' ba*
public const char Pipe = '|'; // Chain two expressions.
public const char Equal = '='; // ' = escape the following character and treat it as a regular character
public const char Escape = '\''; // Escape a special character
public const char Missing = '-';
public const char Exists = '+';
public const char ExpPathSeparator = '/';
The name portion of the clause can be a well known enumeration that a certain technology (i.e. HTML, XAML) recognizes as a certain search pattern. For example, in HTML:
is interpreted by the HTML search tree as a specific search that requires XPATH interpretation.
is interpreted by the XAML search tree as a specific UI Automation id that needs to be matched.
Each technology (HTML, Silverlight, etc.) will define its own FindExpression that inherits from the base FindExpression object. All FindExpression's need only one constructor to define any type of search.
Find the HTML element with an id that ends with 'sam' and also has a class attribute that contains bar and also has a text content that does not contain foo.
HTMLFindExpression expr = new HTMLFindExpression("id=?sam","class=~bar","textcontent=!foo");
We can also define chained identification by inserting a '|' as one of parameters on the constructor. The point where the '|' appears is the chains separation points.You can insert as many chains as you need in one FindExpression.
Find the table where ID equals 'bar' then inside of that element find the 'td' element at index 0, then inside of that find the first image with src containing 'png'.
HTMLFindExpression expr = new HTMLFindExpression("id=bar","|","tagindex=td:0","|","tagname=img","src=~png");
You can also describe a certain hierarchal constraint to be applied against that FindExpression so that the decision on whether a specific element matches a specific translator or not is not solely based on the tag but also takes into consideration its hierarchical position. For example a tag that looks like <div class='foo' /> might be part of a grid while at the same time other elements on the page could contain that tag. The only way to distinguish whether that tag is part of the grid is to inspect its parent or child hierarchy.
FindExpression's natively support hierarchy constraints. A FindExpression can have 0-n HierarchyConstraint objects associated with it.
A hierarchy constraint contains two pieces of data:
- A Hierarchy path => The path from the target element. This is expressed as a list of integers. for example -1,1 means: The first child of the parent. [negative=up/parent index, positive=down/child index]
- A FindExpression => This is the expression to match at the target of the path reference.
For example, suppose we have the following HTML code snippet to deal with:
Now we want to match the p tag that has class='bar' and its parent's parent has an id='foo1'.
// This expression will locate both p tags at  & 
HtmlFindExpression expression =
// Create a hierarchy constraint
HtmlFindExpression parentExpr =
// matches 
// Add the constraint to the original expression.
// -2 signifies two parents up.
// Note that HierarchyConstraints are  based. Zero signifies the target element or reference point.
' This expression will locate both p tags at  & 
' Create a hierarchy constraint
' matches 
' Add the constraint to the original expression.
' -2 signifies two parents up.
' Note that HierarchyConstraints are  based. Zero signifies the target element or reference point.
The resulting expression now will matche  ONLY.
Here is a more involved scenario:
If we wish the constraint to match the first child of the parent's parent (i.e. ) and to be an 'img' instead, that is also doable.
The path in that case is -2, 1 => Two parents up, one down at index 1. (Or -1, -1, 1, both are the same) and the second FindExpression will be "TagName=img" instead of "id=foo1".
The following is a list of the types that can appear on the left side of the expression in HtmlFindExpressions:
- Any valid attribute name (id, name, etc.)
- TextContent - search for an element in which has text that matches the specified expression.
- InnerText - search for an element in which the InnerText matches the specified expression.
- InnerMarkup - search for an element in which the InnerMarkup matches the specified expression.
- OuterMarkup - search for an element in which the OuterMarkup matches the specified expression.
- StartTagContent - search for an element in which the StartTagContent matches the specified expression.
- NodeIndexPath - search for an element having the specified NodeIndexPath.
- TagName - search for an element in which the TagName matches the specified expression.
- TagIndex - search for an element at the specified zero based TagIndex.
- XPath - search for an element at the specified XPath expression.
The following is a list of the types that can appear on the left side of the expression in XamlFindExpressions:
- AutomationId - search for an element having an automation ID of a specific value.
- TextContent - search for an containing or not containing some text in the element.
- XamlTag - search for an element of a specific type.
- XamlTagBase - search for an element that extends a specific type
- Name - search for an element having a name of a specific value.
- TagIndex - the zero based index value of a specific tag.
- XamlPath - Uses a XAML path expression like XamlPath=/radtabcontrol[automationid=Tabs]/grid/raddockpanel/layouttransformcontrol[name=HeaderDockedElement]
For more information on using XamlFindExpressions, see Locating Elements.