Data Source Events
The InfoPath object model consists of objects that expose properties, methods, and events. InfoPath's programming model has two kinds of events: data source events, and form events. Because InfoPath's programming model emphasizes the role of events, let's take a look at the data source events first, and then some of the useful form events, properties, and methods of the various objects.The number of data source events is small compared to the number of form events, but they are arguably the most powerful. Typically, most of the code behind a form involves sinking data source events.Three data source events raise sequentially in the order listed here:
- OnBeforeChange
- OnValidate
- OnAfterChange
Each of these events can be raised on any data node (that is, element, attribute, or group of elements) in the data source.
Although the events are always raised in this order, InfoPath does not guarantee that the events will be raised one immediately after the other. For example, if you have a hierarchy and have event handlers for different levels in the hierarchy, you might not see the events handled immediately after each other for a given data source node. You might see OnBeforeChange raise for a group first, then OnBefore-Change handled next for a field, and then OnValidate for the group, and so on. |
Creating an Event Handler
How do you create an event handler for a particular data node? For example, suppose you have a mortgage application form and you want to handle the OnBeforeChange event for the telephone number HomePhone. Using the InfoPath designer, click the drop-down button on the data node called HomePhone and choose Properties, and then the Validation and Event Handlers tab, as shown in Figure 12-7.
Figure 12-7. Selecting a data source node and showing the Properties dialog.
[View full size image]

You might want to start from a data-bound control to get to a data node for which you want to handle an event. If the data node is bound to a control, you can get to the same dialog shown in Figure 12-7 by first double-clicking the data-bound control in the view to get to its Properties dialog, as shown in Figure 12-8.
public void HomePhone_OnBeforeChange(DataDOMEvent e)
{
// Write your code here. Warning: Ensure that the constraint you
// are enforcing is compatible with the default value you set
// for this XML node.
}
Figure 12-8. Selecting a control's properties to handle a data event.

The OnBeforeChange Event
The OnBeforeChange event fires before the change is made to the underlying XML data. If you want to abort the change, the OnBeforeChange event is your only chance; by the time the OnValidate event is raised, the change has already been made to the underlying data source.To reject the change to the data, set the ReturnStatus property of the DataDOMEvent argument e to false. When ReturnStatus is set to false, InfoPath will show an error dialog informing the user that the change is not allowed.Several additional useful properties are associated with the DataDOMEvent object. The Operation property returns a string set to "Delete", "Update", or "Insert". This tells you whether the user is deleting data, updating data, or inserting new data. The ReturnMessage property accepts a string that is shown in a dialog when the change is rejected. The NewValue property returns a string for the new value of the data node that was changed. The OldValue property returns a string for the value of the data node before it was changed.Listing 12-4 shows an OnBeforeChange event handler that validates that an e-mail address is in a valid format. In Listing 12-4, we first check the DataDOMevent object's Operation property to make sure we are not in a delete operation. If we are in a delete operation, the NewValue property would be null. We then validate the e-mail address returned by the NewValue property by using a regular expression. If the change is not matched by our regular expression, we set ReturnStatus to false and set ReturnMessage to the message text we want InfoPath to use in the error dialog.
Listing 12-4. An OnBeforeChange Event Handler
You cannot change the data source itself from within the event handlerfor example, you cannot set the NewValue property to a different string. InfoPath "locks" the data source to make it read-only for the duration of the event, to prevent the scenario where one event handler attempts to change the data, triggering another change event handler, which might then trigger yet another change event handler, and so on. Making the data source read-only while the event sink runs prevents these "event storm" scenarios.
public void Address_OnBeforeChange(DataDOMEvent e)
{
if (e.Operation == "Delete") // only handle update and insert
return;
string newEmail = e.NewValue;
if (newEmail.Length > 0)
{
Regex emailRegEx = new Regex(
@"^[a-z][\w\.-]*[a-z0-9]@[a-z0-9][\w\.-]*" +
@"[a-z0-9]\.[a-z][a-z\.]*[a-z]$",
RegexOptions.IgnoreCase);
e.ReturnStatus = emailRegEx.IsMatch(newEmail);
e.ReturnMessage = "Please use a valid email address.";
}
}
Data source change events are fired when the form loads and the data source is first created. If you set the DataDOMEvent object's ReturnStatus property to false during this data source creation phase, the form will fail to load. Use caution when writing an OnBeforeChange event handler. |
The OnValidate Event
By the time the OnValidate event raises, the new value has already been written into the data source. The most common reason to sink an OnValidate event is to implement error handling.A form error is typically shown in an InfoPath form by a red dashed "error visual" rectangle surrounding the control. For instance, if you require that a telephone number include the area code, you might use an error visual rectangle to indicate an improper format, as shown in Figure 12-9.
Figure 12-9. A data validation error shown in InfoPath with a red dashed rectangle.

Listing 12-5. An OnValidate Event Handler That Uses the DataDOMEvent Object's ReportError Method and the XDocument Object's Errors Collection
public void HomePhone_OnValidate(DataDOMEvent e)
{
// Ensure that the format is "xxx-xxx-xxxx"
if (e.NewValue == null)
return;
bool siteIndependent = false;
int errorCode = 0;
string modal = "modal";
string newPhone = e.NewValue;
if (newPhone.Length != 12)
{
// Tell InfoPath what node caused the error, whether the error
// is associated with this node, what the short and long error
// messages should be, and whether to produce a modal or
// modeless error dialog:
e.ReportError(e.Site, "Phone number format error",
siteIndependent, "Phone number expected format is xxx-xxx-xxxx.",
errorCode, "modeless");
}
else
{
int indexOfHyphen = newPhone.IndexOf('-');
if (indexOfHyphen != 3)
{
thisXDocument.Errors.Add(e.Site, "NoExpectedHyphen",
"No hyphen found", "Expected a hyphen after 3 digits.",
errorCode, modal);
}
else
{
indexOfHyphen = newPhone.IndexOf('-', indexOfHyphen + 1);
if (indexOfHyphen != 7)
{
thisXDocument.Errors.Add(e.Site, "NoExpectedHyphen",
"Second hyphen not found",
"Expected a hyphen after 6 digits.",
errorCode, modal);
}
}
}
}
Site Versus Source
Another thing to note in Listing 12-5 is the code passes the Site property of the DataDOMEvent object to give ReportErrors and Errors.Add the data node where the error occurred. The Site property of the DataDOMEvent object refers to the data node currently processing the validation event (that is, the data node to which the event handler is listening). The DataDOMEvent object's Source property refers to the data node that changed and triggered validation. Remember that events can "bubble up" from child nodes to parent nodes; if you are sinking the OnValidate event of a parent node and the user changes a child node, the Site will refer to the parent node handling the event, and the Source will refer to the child node that triggered the event in the first place.
The Site and Source properties and the Errors.Add and ReportError methods all require the domain security level. |
The OnAfterChange Event
In OnBeforeChange and OnValidate events, the data source is read-only and cannot be modified by your event handler code. When can your code modify the data source? Code you write in an OnAfterChange event handler is allowed to edit the data source if InfoPath is not raising the OnAfterChange event for an undo or redo operation invoked by the user. Your OnAfterChange event handler can detect whether an undo or redo resulted in the event being raised by checking the DataDOMEvent's IsUndoRedo property.If you directly update the data node that your event handler corresponds to, use cautionotherwise you could create infinite recursion. Listing 12-6 shows a simple OnAfterChange event handler that directly changes the data node it is handling the event for by setting e.Site.text to a new value. It prevents recursion by first checking to see whether e.Site.text is already set to the new value. It also checks the IsUndoRedo property to make sure OnAfterChange was not raised as a result of an undo or redo.
Listing 12-6. An OnAfterChange Event Handler That Updates the Data in the Node for Which It Is Handling the OnAfterChange Event
[InfoPathEventHandler(MatchPath="/my:myFields/my:someField",
EventType=InfoPathEventType.OnAfterChange)]
public void someField_OnAfterChange(DataDOMEvent e)
{
if (e.IsUndoRedo)
return;
if (e.Site.text == "newFieldValue")
return; // prevents recursion
e.Site.text = "newFieldValue";
}