The purpose of the Toolbar is to give rule authors an easy to use UI that provides basic rule management functionality such as creating, editing, and deleting rules. It also can be used to select (load) an existing rule, and name and describe new or existing rules.
If shown, the Toolbar always appears on top of the Rule Area. It consists of several UI elements:
Rules menu. This menu provides options to create new rules and load existing ones, if there are any. The number of options to create new rules and labels of those options depends on the Mode property of the RuleEditor class. If the Mode is set to RuleType.Execution, the Toolbar will include two options - New execution type rule... and New evaluation type rule.... Selecting one of those options will clear the Rule Area for the new rule of the selected type. If the Mode is set to RuleType.Evaluation, the Toolbar will include only one option named New rule.... Selecting that option will clear the Rule Area for a new rule of evaluation type. You can easily change the labels of all these menu options to your own values by employing the Help XML.

You can also include options in this menu to load existing rules for editing. It is entirely up to you, the developer,to decide which rules to load in this menu, as long as they correlate with the current value of the RuleEditor.Mode. You can load any type of rule if the Mode is set to RuleType.Execution. But Web Rule may not work properly if you load any execution type rule into the Rules menu while the Mode is set to RuleType.Evaluation (its default value). Both of our demo projects provide the code to load rules into the Rules menu of the Toolbar. Here is an excerpt from the ASP.NET demo (from OnLoad event handler in the /Default.aspx.cs code-behind file):
this.ruleControl.ToolBarRules = this.ruleControl.Mode == RuleType.Execution ?
Storage.GetAllRules() : this.ruleControl.ContextMenuRules;
The MVC demo uses the LoadMenuRules() method declared in PostController and used in the controller's constructor, as well as almost all POST actions of that controller, to load existing rules into the Rules menu:
private void LoadMenuRules()
{
ViewBag.ToolBarRules = this.storage.GetAllRules();
...
}
In ASP.NET you don't need to do anything else in order to pass the rules to the client - everything else will be done automatically. Because the view and the controller are semi-disconnected in MVC, you need to tell the RuleEditor which collection of rules to use in the Rules menu on the client:
<div>
@{
Html.CodeEffects().RuleEditor()
.Id("ruleEditor")
.SaveAction("Save", "Post")
.DeleteAction("Delete", "Post")
.LoadAction("Load", "Post")
.Mode(RuleType.Execution)
.ToolBarRules(ViewBag.ToolBarRules)
.Rule(ViewBag.Rule)
.Render();
}
</div>
RuleEditor.ToolBarRules is the property that you need to populate for rules to appear in the Rules menu on the client. It's of the ICollection<MenuItem> type.
In ASP.NET, selecting one of the existing rules from the Rules menu raises the LoadRule event of the RuleEditor class, passing the ID of the selected rule to the server with the RuleEventArgs arguments class.
private void LoadRule(object sender, RuleEventArgs e)
{
this.ruleControl.LoadRuleXml(Storage.LoadRuleXml(e.RuleID));
}
In MVC, selecting one of the rules posts the view and passes the rule ID to the LoadAction as a parameter.
[HttpGet]
public ActionResult Load(string id)
{
// Load rule from storage
string ruleXml = this.storage.LoadRuleXml(id);
// Create a new model and store it in the bag
ViewBag.Rule = RuleModel.Create(ruleXml, typeof(SourceObjectType));
return View("Index");
}
Name text box. Rule authors can name rules by typing the rule name into the Name text box. The name is stored in Rule XML. By default it's a required field unless the Toolbar is hidden. Set RuleEditor.RuleNameIsRequired to False to make this field optional. The default "empty" label of the box can easily be changed with the Help XML.
Description text box. Rule authors can also give rules short descriptions by typing them into the Description box. This field is optional. Web Rule displays rule descriptions in the Help String when rule authors move through the list of existing rules in the Rules menu with the mouse or keyboard. Web Rule also shows descriptions of reusable rules when a rule author hovers the mouse over the rule in the Rule Area. The default "empty" label of the box can easily be changed with the Help XML.
Save button. The Save button allows rule authors to notify the server code that the current rules needs to be saved. Web Rule does not actually save the rule for you since it has no knowledge of the infrastructure or systems involved in your rule processing. Instead, it just passes the rule data to the server and makes it available to your code as a compact string-based XML document, together with some other useful data. It's up to you how to handle that data.
In ASP.NET, clicking the Save button raises the SaveRule event of the RuleEditor class. The event arguments of the SaveEventArgs type contain everything you need to successfully save the rule, including Rule XML as a separate document or just a single node.
private void SaveRule(object sender, SaveEventArgs e)
{
if(this.ruleControl.IsEmpty || !this.ruleControl.IsValid)
return;
string file = "C:\\MyRules.xml";
if(!File.Exists(file)) File.WriteAllText(file, e.RuleXmlAsDocument);
else
{
// Load the rules document (in this example
// all rules are stored in one big file)
XmlDocument xml = new XmlDocument();
xml.Load(file);
// Create a temp node and give it the rule as its inner XML
XmlElement temp = xml.CreateElement("temp");
temp.InnerXml = e.RuleXmlAsNode;
// Check if the document already contains this rule
foreach(XmlNode node in xml.DocumentElement.ChildNodes)
{
if(node.Attributes["id"].Value == e.RuleID)
{
node.InnerXml = temp.FirstChild.InnerXml;
// Replace the whole doc with the new one
File.Delete(file);
xml.Save(file);
// We are done
return;
}
}
// This is a new rule, just append it to the doc
xml.DocumentElement.AppendChild(temp.FirstChild);
File.Delete(file);
xml.Save(file);
}
}
In MVC, clicking the Save button posts the view and passes an instance of the RuleModel to the SaveAction of the controller. Notice that even though the type of the parameter is RuleModel, its name must be the server ID of the RuleEditor that you used in the view (see the MVC snippet in the Rules menu section of this topic). This is the default behavior of MVC when a custom TypeConverter is used (and it is used in Web Rule). We certainly could have let you incorporate the rule data into your own view model and enjoy the "true" MVC experience. But that would complicate things to the extreme without gaining anything in return. Remember that ASP.NET MVC is not really a "component-friendly" pattern. So, either you code about 30% of Web Rule MVC yourself, or Web Rule frees you from that task by enforcing some simple naming conventions.
// "ruleEditor" is the ID of the RuleEditor instance declared in the view.
[HttpPost]
public ActionResult Save(RuleModel ruleEditor)
{
// At this point the rule model cannot possibly know which type
// to use as its source object
// You must "bind" the type of the source to the instance of the RuleModel
ruleEditor.BindSource(typeof(Patient));
ViewBag.Rule = ruleEditor;
if (ruleEditor.IsEmpty() || !ruleEditor.IsValid())
{
ViewBag.Message = "The rule is empty or invalid";
return View("Index");
}
// This code sample assumes that we store all our rules in a single XML file
string file = "C:\\MyRules.xml";
if(!System.IO.File.Exists(file))
System.IO.File.WriteAllText(file, ruleEditor.GetRuleXml());
else
{
XmlDocument xml = new XmlDocument();
xml.Load(file);
XmlElement temp = xml.CreateElement("temp");
// See our MVC demo for implementation of the ExtractRuleNode method
temp.InnerXml = this.ExtractRuleNode(ruleEditor.GetRuleXml()).OuterXml;
// Check if a rule with the same ID already exists
foreach(XmlNode node in xml.DocumentElement.ChildNodes)
{
if(node.Attributes["id"].Value == ruleEditor.Id)
{
node.InnerXml = temp.FirstChild.InnerXml;
System.IO.File.Delete(file);
xml.Save(file);
return View("Index");
}
}
xml.DocumentElement.AppendChild(temp.FirstChild);
System.IO.File.Delete(file);
xml.Save(file);
}
return View("Index");
}
Delete button. The Delete button allows rule authors to notify the server code that the current rule needs to be saved. As with every other piece of Toolbar's functionality, Web Rule does not (and cannot) actually delete your rule. Instead, it lets your code know which rule the users wants to delete. This button is only visible on the Toolbar when a previously saved rule is loaded into the Rule Area.
In ASP.NET, clicking the Delete button raises the DeleteRule event of the RuleEditor class, passing the ID of the rule that needs to be deleted with the RuleEventArgs arguments class.
private void DeleteRule(object sender, RuleEventArgs e)
{
// This code sample assumes that we store all our rules in a single XML file
string file = "C:\\MyRules.xml";
XmlDocument xml = new XmlDocument();
xml.Load(file);
// Check if this rule is still referenced in other rules
if(e.IsEvaluationTypeRule == null || !(bool)e.IsEvaluationTypeRule)
{
foreach(XmlNode node in xml.DocumentElement.ChildNodes)
if(node.Attributes["id"].Value != e.RuleID &&
node.InnerXml.Contains(e.RuleID))
throw new RuleException(
"The rule is still referenced in other rules.");
}
// Find and remove the rule
foreach(XmlNode node in xml.DocumentElement.ChildNodes)
{
if(node.Attributes["id"].Value == e.RuleID)
{
xml.DocumentElement.RemoveChild(node);
File.Delete(file);
xml.Save(file);
return;
}
}
throw new RuleException("Could not find the rule with ID " + e.RuleID);
}
In MVC, clicking the Delete button posts the view and passes the rule ID to DeleteAction as a parameter.
[HttpGet]
public ActionResult Delete(string id)
{
string file = "C:\\MyRules.xml";
XmlDocument xml = new XmlDocument();
xml.Load(file);
// Check if this rule is still referenced in other rules
foreach(XmlNode node in xml.DocumentElement.ChildNodes)
if(node.Attributes["id"].Value != id && node.InnerXml.Contains(id))
throw new Exception("The rule is still referenced in other rules.");
// Find and remove the rule
foreach(XmlNode node in xml.DocumentElement.ChildNodes)
{
if(node.Attributes["id"].Value == id)
{
xml.DocumentElement.RemoveChild(node);
System.IO.File.Delete(file);
xml.Save(file);
return View("Index");
}
}
// Create a new model and store it in the bag
ViewBag.Rule = RuleModel.Create(typeof(Patient));
return View("Index");
}