Conditions

Add-ins may use ''conditions'' to register nodes in an extension point which are only visible under some contexts. Conditions can only be used in XML manifests.

For example, an add-in registering a custom menu option to the main menu of our sample text editor might want to make that option visible only for some kind of files. To allow add-ins to do this kind of check, the host application needs to define a new condition. Conditions are defined like this:

<Addin namespace="TextEditor" id="Core" version="1.0">
	...
	<ConditionType id="Openfile" type="TextEditor.OpenFileCondition" />
	...
</Addin>

The following table describes the attributes and elements shown above:

XMLDescription
/Addin/ConditionTypeDeclares a new condition type.
/Addin/ConditionType/@idIdentifier of the condition type. This is the ID to be used in extensions to reference this condition.
/Addin/ConditionType/@typeCLR type that implements this condition.


This condition defined in the add-in root can be referenced by add-ins like this:

<Addin namespace="TextEditor" id="Xml">
	...
	<Extension path = "/TextEditor/MainMenu/Edit">
		<Condition id="OpenFile" extension="xml,config">
			<MenuSeparator insertafter="Paste" />
			<MenuItem label="Format XML" commandType="TextEditor.Xml.FormatXmlCommand" />
		</Condition>
	</Extension>
	...
</Addin>

Meaning that a separator and new "Format XML" command will be added after the "Paste" command, but only if the current open file has the extension ".xml" or ".config".

Extension points are dynamically updated when the status of a condition changes. Nodes matching the new condition status will be added, and nodes which do not match the condition will be removed.

Conditions are implemented using a subclass of Mono.Addins.ConditionType. For example, OpenFileCondition might be implemented like this:

namespace TextEditor
{
	public class OpenFileCondition: ConditionType
	{
		public OpenFileCondition ()
		{
			// It's important to notify changes in the status of a condition,
			// to make sure the extension points are properly updated.
			TextEditorApp.OpenFileChanged += delegate {
				// The NotifyChanged method must be called when the status
				// of a condition changes.
				NotifyChanged ();
			};
		}
		
		public override bool Evaluate (NodeElement conditionNode)
		{
			// Get the required extension value from an attribute,
			// and check againts the extension of the currently open document
			string val = conditionNode.GetAttribute ("extension");
			if (val.Length > 0) {
				string ext = Path.GetExtension (TextEditorApp.OpenFileName);
				foreach (string requiredExtension in val.Split (','))
					if (ext == "." + requiredExtension)
						return true;
			}
			return false;
		}
	}
}

The add-in engine will create an instance of OpenFileCondition when needed, and will call Evaluate to get the result for a specific condition node.

Extension Point Conditions

It is also possible to define conditions which are local to extension points, which means that those conditions will only be usable in extensions of that extension point.

This kind of extension point are defined like in the following example:

<Addin namespace="TextEditor" id="Core" version="1.0">
	...
	<ExtensionPoint path = "/TextEditor/ContextMenu">
		<ExtensionNode type="TextEditor.SubmenuNode"/>
		<ConditionType id="FileName" type="TextEditor.OpenFileLocalCondition" />
	</ExtensionPoint>
	...
</Addin>

There are two important differences between extension point (local) conditions and global conditions:
  • Local conditions can only be used in the extension point that declares them.
  • ConditionType instances for local conditions are not created by the add-in engine. The host application must register an instance of the ConditionType before trying to query the node.

For example, the class OpenFileLocalCondition could be defined like this:

namespace TextEditor
{
	public class OpenFileLocalCondition: ConditionType
	{
		string fileName;

		public OpenFileLocalCondition (string fileName)
		{
			this.fileName = fileName;
		}
		
		public override bool Evaluate (NodeElement conditionNode)
		{
			// Get the required extension value from an attribute,
			// and check againts the extension of the currently open document
			string val = conditionNode.GetAttribute ("type");
			if (val.Length > 0) {
				string ext = Path.GetExtension (fileName);
				foreach (string requiredExtension in val.Split (','))
					if (ext == "." + requiredExtension)
						return true;
			}
			return false;
		}
	}
}

At run-time, condition values can be specified before querying an extension point by using an Extension Context. For example:

// Create an extension context to be used to query the extension point using
// a specific set of conditions. You could have several contexts with different
// condition values.
ExtensionContext ctx = AddinManager.CreateExtensionContext ();

// Create and register the extension point condition
OpenFileLocalCondition condition = new OpenFileLocalCondition (someFileName);
ctx.RegisterCondition (condition);

// Query the extension point
foreach (ExtensionNode node in ctx.GetExtensionNodes ("/TextEditor/ContextMenu"))
	(...)

Nested Conditions

Nested conditions like in the following are allowed:

<Addin namespace="TextEditor" id="Xml">
	...
	<Extension path = "/TextEditor/MainMenu/Edit">
		...
		<Condition id="OpenFile" extension="xml,config">
			<!-- The following nodes will be added only for .xml and .config files -->
			<MenuSeparator insertafter="Paste" />
			<MenuItem label="Format XML" commandType="..." />
			<MenuItem label="Check DTD" commandType="..." />
			...
			<Condition id="OpenFile" extension="config">
				<!-- This node will be added for .config files only -->
				<MenuItem label="Insert config section" commandType="..." />
			</Condition>
		</Condition>
		...
	</Extension>
	...
</Addin>

Complex Conditions

Extensions can use the ComplexCondition element to build conditions using And and Or operators. For example:

<Addin namespace="TextEditor" id="Xml">
	...
	<Extension path = "/TextEditor/MainMenu/Edit">
		...
		<ComplexCondition>
			<!-- The first child must be a condition operator -->
			<Or>
				<!-- A condition operator can contain conditions or other operators -->
				<Condition id="OpenFile" extension="xml" />
				<Condition id="OpenFile" extension="config" />
				<And>
					<Condition id="OtherCond1" value="1" />
					<Condition id="OtherCond2" value="2" />
				</And>
			</Or>
			<!-- Starting here, nodes to be added if the condition is satisfied -->
			<MenuSeparator insertafter="Paste" />
			<MenuItem label="Format XML" commandType="TextEditor.Xml.FormatXmlCommand" />
		</ComplexCondition>
		...
	</Extension>
	...
</Addin>

The fist child of a ComplexCondition must be a condition operator (Or or And). The other children of the are nodes to be added if the complex condition evaluates to ''true''.

Last edited Aug 18, 2010 at 8:58 AM by slluis, version 4

Comments

No comments yet.