Form Events, Properties, and Methods
Data source events prove very useful for ensuring that data constraints are maintained and taking action when the user makes changes to data. InfoPath also provides a form object model that you can use to further customize the behavior of your form. Some of the scenarios the form object model enables are as follows:
- Consider the earlier example of a form that displays interest rate quotes to the user filling out the form. You might want to create an "Administrator" view of the form that allows authorized people to change the quoted interest rates.
- You might want to integrate context-sensitive help as the user navigates through your form.
- You might want a custom task pane to extend and further customize the experience of filling out the form.
- You might want to customize how your forms are saved and submitted. Instead of submitting via a data connection or allowing a user to save to any location, you could restrict where the form is saved.
The remainder of this chapter examines the form object model and discusses how to create a custom task pane for a form. The InfoPath forms object model contains many events, properties, and methods. This book discusses only some of the most commonly used parts of the InfoPath forms object model.
Button Events and View Switching
A view is a surface on which you insert controls and form content in the designer; it is what the user looks at while filling out the form. Lengthy forms are often composed of multiple views. In addition, data being edited can be displayed in multiple viewsfor example, you might have a timecard that can be viewed in a less-detailed view for someone who wants to quickly enter information. A more detailed view might also be available for a manager trying to generate end of pay-period reports. You can find a list of available views for the data being edited in InfoPath's View menu. A user can switch between various views at will.Switching between views might not be the desired behavior, especially if your views are supposed to be sequential or have dependencies. While designing your form, you have an option to remove the name of a view from the View menu and to prevent users from choosing a particular view. The code behind the form can then switch views programmatically by using the XDocument.View.SwitchView method.Suppose you have a mortgage application with two views. The first view allows the user to fill out contact information: name, phone numbers, and so on. When all the required contact information is filled out, the user can click Go to Mortgage Details button. You can add a button to the form by selecting the Controls task pane in the InfoPath form design view and dragging a button onto the form designer. If you right-click the button and choose Properties, you can then open the Properties dialog shown in Figure 12-10.
Figure 12-10. Creating a button with the Form Designer.
[View full size image]

Listing 12-7. An OnClick Event Handler for a Button That Switches the View
[InfoPathEventHandler(MatchPath="GoToDetailsButton",
EventType=InfoPathEventType.OnClick)]
public void GoToDetailsButton_OnClick(DocActionEvent e)
{
const string FirstNameXPath = "/my:myFields/my:FirstName";
const string LastNameXPath = "/my:myFields/my:LastName";
IXMLDOMDocument mainData = thisXDocument.DOM;
if (string.IsNullOrEmpty(
mainData.selectSingleNode(FirstNameXPath).text) ||
string.IsNullOrEmpty(
mainData.selectSingleNode(LastNameXPath).text))
{
thisXDocument.UI.Alert("Please fill in first and last name.");
}
else
{
thisXDocument.View.SwitchView("Mortgage Details");
}
}
The OnContextChange Event and the Custom Task Pane
Another way to write code to handle form changes is to use the XDocument object's OnContextChange event. What exactly do we mean by "context"?The user can only be interacting with one control at a time; mouse clicks or key presses are handled by the control that has the focus. The context of a form is the data source node bound to the control that presently has the focus.Consider the example of a contact information form. Each text box is bound to a particular node in the data source. As the user filling out the form uses the mouse or keyboard to move the focus from one control to the next on the form, context changes to a different data node, and the XDocument object's OnContextChange event is raised.You could have more than one control bound to the same data node. In that case, if the user were to change the focus from one control to another bound to the same data node, the context change event would not raise because context has not changed. In a repeating control, the OnContextChange event is raised when focus is changed from row to row. However, the OnContextChange event does not indicate the new row position.
Creating a Custom Task Pane
A common way to use OnContextChange event is to integrate a dynamic help system into a form. By detecting when the form is editing a different data node, we can provide help for the data node being edited in the task pane. The first thing we need to do is enable the custom task pane for this form. Choose Form Options from the Tools menu of the InfoPath designer and select the Advanced tab, as shown in Figure 12-11.
Figure 12-11. Enabling and adding resources to the custom task pane.

If you preview this form, you will see that as you select different text boxes the task pane displays the appropriat192 files.
[InfoPathEventHandler(EventType=InfoPathEventType.OnContextChange)]
public void OnContextChange(DocContextChangeEvent e)
{
if (e.Type == "ContextNode")
{
HTMLTaskPane helpTaskPane =
(HTMLTaskPane)thisXDocument.View.Window.TaskPanes[0];
string navigateTo = "GeneralHelp";
string thisNodeName = e.Context.nodeName;
if (thisNodeName == "my:FirstName")
navigateTo = "NameHelp";
else if (thisNodeName == "my:LastName")
navigateTo = "NameHelp";
else if (thisNodeName == "my:HomePhone")
navigateTo = "PhoneHelp";
else if (thisNodeName == "my:WorkPhone")
navigateTo = "PhoneHelp";
helpTaskPane.Navigate(navigateTo);
}
}
Figure 12-12. The custom task pane at runtime.
[View full size image]

Forms in the restricted security level can access the task panes collection, but reading the Context property requires at least the domain security level. |
Focus Versus Selection
What if you want to determine the current context in some event handler other than an OnContextChange handler? The XDocument's View property returns a View object. The View object has a GetContextNodes method that can be called from any event handler. It returns a collection of all the XML nodes that are in context: not just the node bound to the control with the focus, but all of its parent nodes in the data source tree as well.The View object also provides a GetSelectedNodes method that returns the collection of XML nodes bound to the currently selected controls. This is a subtle distinction: Only one control can have the focus at any one time, but a user can select multiple controls.You might be tempted to use the GetSelectedNodes or GetContextNodes methods in a button click handler. Unfortunately, this does not work; as soon as the user clicks the button, the focus and selection change to the button itself.
GetContextNodes and GetSelectedNodes both require at least the domain security level. |
Setting Selection
Two other useful methods on the View object are the SelectNodes and SelectText methods. SelectText takes a single IXMLDOMNode, and SelectNodes takes two IXMLDOMNodes (to define the start and end of a range) to determine what to select. For example, consider the earlier example in which we wrote an OnClick event handler for a button to ensure that the FirstName and LastName fields were not blank before switching views. You could use the SelectText method to select the text box that was blank so that the user could simply start typing into the blank text box to fix the error.
Overriding Submit, Confirm, and Save
So far you have seen how to use data source and form events to ensure that data entered by users is valid, reacts to users navigating around the form, and so on. This chapter has not yet discussed what happens to the data in the form when all the information is entered and validated. Somehow, the data must be saved to disk or submitted to a server somewhere.Suppose you want to prevent the user from specifying a destination for the saved data. Rather, when the user is done with the form, you want to ensure that the data is always saved to a particular shared directory on your intranet. You can accomplish this by handling the OnSubmitRequest event and writing code to force the data to be saved to that location.The first thing you need to do is to disallow users from saving. Go to the Tools menu in the InfoPath designer, then Form Options, and then click the Open and Save tab. Uncheck the Save and Save As check box, as shown in Figure 12-13.
Figure 12-13. Disabling Save and Save As for a form.

Figure 12-14. Creating a custom event handler for the OnSubmitRequest event.

Listing 12-9. An OnSubmitRequest Event Handler That Forces the Form to be Saved to a Particular Network Share
InfoPath uses the ReturnStatus flag to determine whether the OnSubmitRequest event succeeded. It is not necessary to set the ReturnStatus flag to true in this example when closing the form window because the runtime is immediately shut down when the form window is closed.
[InfoPathEventHandler(EventType=InfoPathEventType.OnSubmitRequest)]
public void OnSubmitRequest(DocReturnEvent e)
{
XdConfirmChoice submitChoice;
if (thisXDocument.Errors.Count > 0)
{
submitChoice = thisXDocument.UI.Confirm(
"Errors exist on the form. Continue submitting?",
XdConfirmButtons.xdYesNo);
}
else
{
submitChoice = thisXDocument.UI.Confirm(
"Are you sure you want to submit?", XdConfirmButtons.xdYesNo);
}
if (submitChoice == XdConfirmChoice.xdYes)
{
string firstName = thisXDocument.DOM.selectSingleNode(
"/my:myFields/my:FirstName").text;
string lastName = thisXDocument.DOM.selectSingleNode(
"/my:myFields/my:LastName").text;
string fileName = firstName + "_" + lastName + ".XML";
thisXDocument.SaveAs(@"\\myShare\forms$\" + fileName);
thisXDocument.UI.Alert("Thank you, " +
firstName + "! You will be contacted shortly.");
thisApplication.ActiveWindow.Close(true /*force*/);
// No need to set ReturnStatus because InfoPath closes
}
else
e.ReturnStatus = false;
}
The call to Confirm requires the domain security level and the call to SaveAs requires the full trust security level. Therefore, we will need to either digitally sign or register the form template to get full trust permissions. |