SP-1 Several earlier chapters discuss the need to add programming code to Microsoft Office InfoPath 2003 forms to handle tasks that you can t accomplish with InfoPath s declarative programming model. The declarative programming features added by InfoPath 2003 Service Pack 1 (SP-1) ”such as the Insert Formula dialog box, event-based rules, and user roles ”substantially reduce the need to add programming code to forms. The InfoPath 2003 release version supported only JScript or VBScript programming with the Microsoft Script Editor (MSE). SP-1 lets you create InfoPath 2003 Projects, which substitute managed Visual Basic .NET or C# code for script. You must install the Toolkit to take advantage of SP-1 s managed code features.
The chapters in Part IV use Visual Basic .NET for all procedures, because most Office developers are accustomed to writing VBA code to automate Office applications. Visual Basic .NET has full parity with C# as a programming language, although each language has a few features that are missing in the other. For example, Visual Basic .NET supports With ... End With constructs; C# doesn t. C# offers XML comments, which aren t available in Visual Basic .NET. Building .NET assemblies with either language generates identical Microsoft Intermediate Language (MSIL) code, which the just-in-time (JIT) compiler converts to the same machine-language instructions. Neither language offers a significant performance benefit or penalty.
Programming InfoPath forms with JScript or VBScript
Regardless of your scripting experience, Microsoft Visual C# and Visual Basic .NET are Microsoft s preferred programming languages (in that order, unfortunately ), and they represent the future of Microsoft Windows application development. Future versions of InfoPath are likely to be full- fledged .NET implementations ; if so, managed code for implementing business logic probably will be a requirement, not an option.
The primary distinction between managed .NET code and script is how the code executes: .NET code is compiled, and script is interpreted. A language compiler translates .NET source code into MSIL, and the Common Language Runtime (CLR) JIT-compiles the MSIL to executable (machine) code for a specific processor, such as the 32-bit Intel Pentium or 64-bit AMD Opteron series. The JIT compilation process generates machine code on an as-needed basis; the first time a subprocedure or function executes, the JIT-compiled machine code is cached for reuse. An interpreter reads successive lines of script code and translates each line to machine code every time the script runs. In most cases, compiled code executes faster than interpreted code.
The most important benefits of managed code for InfoPath developers are much easier debugging and early object binding, which enables Microsoft IntelliSense in Visual Studio for statement completion. Building and running managed code opens the form in preview mode. When execution reaches a breakpoint, Visual Studio receives the focus to let you determine the value of variables or object properties at that point. Continuing past the breakpoint returns the focus to the form preview. Another advantage of managed code is that your source code isn t easily accessible to InfoPath users or your competitors . The template stores a reference to an assembly , which is a dynamic-link library (DLL) that contains program metadata and MSIL, not source code. Although it s possible to decompile MSIL, you can make it very difficult for others to reverse-engineer your MSIL code with the Dotfuscator obfuscation tool that s included with Visual Studio.
Substituting managed code for script also enables strong typing, which isn t turned on by default for Visual Basic .NET. To enforce strong typing, you must add an Option Strict On statement at the beginning of the code or set a language compiler option to enforce strong typing for all Visual Basic .NET projects. When you declare all variables with their data or object types, the language compiler won t let you set the variable s value to a different type. Strong typing prevents declaring variables of the Object data type, the .NET equivalent of VBA s Variant data type. JScript and VBScript are loosely typed; variables assume the type of the values you assign to them and can change data or object types without notice. Strong typing improves code execution performance and minimizes the potential for run-time errors.
The .NET Framework provides myriad useful classes and methods that aren t present in JScript or VBScript. For example, the InfoPath sample forms require custom getDateString and getTimeString functions to return date and time in the ISO 8601 pattern that s required by fields of the xsd:date and xsd:dateTime datatypes. The .NET Framework s DateTime class provides standard date/time formatters to return ISO 8601, RFC1123, and many other patterns. RFC1123 is the date format used in the RSS 2.0 examples in this and the preceding chapters. The sections Responding to OnAfterChange Events and Taking Action Based on the Value of Another Field, later in this chapter, contain ISO 8601 formatting examples. The section Adding Managed Code to an Existing Form demonstrates RFC1123 formatting.
A minor downside of adding managed code to InfoPath templates is the requirement to install the run-time version of the .NET Framework 1.1 on users computers to provide required classes, and JIT-compile the assembly when the form loads. The 108 MB run-time .NET Framework 1.1 is freely distributable so you can put the Setup.exe file on a server share and instruct users to install it, or users can download .NET Framework 1.1 from the Microsoft Windows Update site, at windowsupdate.microsoft.com .
After you install the Toolkit, you can add managed code to an imported copy of an existing form or create a new form in Visual Studio s integrated development environment (IDE). When you open the New Project dialog box, you ll find that the Toolkit has added a new Microsoft Office System Projects icon to the Project Types list, which contains subfolders for Visual Basic and Visual C# projects and an InfoPath Form Template icon, as shown in Figure 15-1. (Installing the VSTO also adds this folder and three template icons for Excel and Word 2003.) If you re using Visual Basic .NET Standard, only Visual Basic Projects appear in the list.
Figure 15-1: The Toolkit adds an InfoPath Form Template icon for Visual Basic and, if installed, Visual C#, and supplies default values for the project name and its folders.
Accepting default settings for the project and clicking OK opens the single screen of the Microsoft Office Project Wizard, which lets you choose between importing a copy of an existing form or creating a new form. Importing a form with JScript or VBScript code disables script operation, but the .js or .vbs files remain in the template file for reference when you replace the script s methods with Visual Basic .NET code. If you select the Create A New Form Template option, the project opens with an empty InfoPathProject1 form in design mode in front of Visual Studio s InfoPathProject1FormCode window, as shown in Figure 15-2. Solution Explorer displays the working file set created by the Toolkit.
Figure 15-2: Creating a new form template with the default project name opens a new InfoPathProject1 template in design mode and displays the files added to the project by the Toolkit in Solution Explorer.
The Toolkit adds project references to the System namespace and the Microsoft.Office.Interop.InfoPath.SemiTrust namespace defined by the primary interop assembly DLL of the same name. Microsoft provides primary interop assemblies (PIAs), which are wrappers for Component Object Model (COM) type libraries, for most Office XP and Office 2003 applications. PIAs install in the global assembly cache (GAC) so they can be shared by all .NET projects. Don t infer from the presence of a PIA that an Office application is .NET-enabled; Microsoft Word 2003, Excel 2003, and InfoPath 2003 SP-1 were the only Visual Studio .NET “enabled Office applications when this book was written.
The Toolkit generates Visual Basic .NET code to create instances of the InfoPath Application and XDocument objects in a _Startup event handler. These two objects are the most important members of the InfoPath object model, which is described in detail in Chapter 16. Figure 15-3 shows the default Visual Basic .NET code added by the Toolkit ”commonly called an event-handling stub ”with empty lines removed and minor edits for readability. To have Solution Explorer show all files in the project after building and running the default Visual Basic .NET code, choose Project, Show All Files. Notice that the file set includes multiple references to extracted template files.
Figure 15-3: Running the default Visual Basic .NET code generated by the Toolkit and choosing Project, Show All Files creates the files and file pointers shown here.
Visual Studio creates all projects in subfolders of your My DocumentsVisual Studio Projects folder by default. If you accept the default values in the New Project dialog box, click OK, select the Create A New Form Template option in the Microsoft Office Project Wizard s only screen, and click Finish, the template generates a set of files and subfolders in a My DocumentsVisual Studio ProjectsInfoPathProject1 folder, which contains only solution (.sln and .suo) files. Your working files, shown in Figure 15-3 s Solution Explorer, are in a nested InfoPathProject1 folder and its subfolders. The files that you deploy after building the project in the default debug mode are located in the My DocumentsVisual Studio ProjectsInfoPathProject1 InfoPathProject1inDebug folder. InfoPathProject1.xsn is the final template file, InfoPathProject1.dll is the managed code assembly, and InfoPathProject1.pdb contains the debugging symbols. When you compile release versions of your projects, .pdb symbols aren t generated.
Changing file names and locations after creating a project is a chancy process at best, so it s a good practice to decide on a final project name and the initial location for the project files before you start a new InfoPath project. Minimizing the length of the project s path makes it easier to check file locations in the file s Properties dialog box. The examples you create in this chapter run from project subfolders of a C:IPProjects projects folder and don t create separate subfolders for the solution (.sln and .suo) files.
Your only access to InfoPath s COM objects that the InfoPath PIA exposes is through event handlers and subprocedures or functions invoked by event handlers. The _Startup event handler of the code behind the form declares two variables ” thisApplication and thisXDocument . These variables represent the current InfoPath form instance and its data source document. XDocument objects contain a UIObject , which has an Alert method to display InfoPath s standard alert message box. A button with a click event handler that opens an alert box is the simplest example of adding event-handling code behind an InfoPath form.
To add a button and an event handler that displays an alert, follow these steps.
Create a new project and add a button event handler
Restoring InfoPath templates that disappear
The event handler s < InfoPathEventHandler (MatchPath:="btnClickEvent", EventType:=InfoPathEventType.OnClick) > attribute delegates handling of the InfoPath OnClick event to the btnClickEvent_OnClick event handler. The e parameter s DocActionEvent interface has three properties: ReturnStatus , Source , and XDocument . The ReturnStatus property is a Boolean value that defaults to True and is useful for handing InfoPath events that accept return values and cancel the action if the return value is False . The Source property returns a read-only IXMLDOMNode instance that represents the form s current data source ” my:fields for the example at this point.
Copying and pasting code snippets
The UI object s Confirm method provides the visual equivalent of a Windows forms message box and offers a choice of OK/Cancel, Yes/No, or Yes/No/Cancel buttons . You can use the return value of the message box to control program flow with an If ... Else ... End If construct. To change the alert to a confirmation message box, replace the alert statement you added in step 9 of the preceding procedure with the following statements or add them from Confirm.txt:
With thisXDocument If .UI.Confirm("The source is " + e.Source.baseName + _ ". Do you want to continue?", XdConfirmButtons.xdYesNo) = _ XdConfirmChoice.xdYes Then e.ReturnStatus = True .UI.Confirm("Continuation confirmed.", _ XdConfirmButtons.xdOKCancel) Else e.ReturnStatus = False End If End With
When you click the button, the upper confirm message box of Figure 15-4 opens. Click Yes to open the message box. Unfortunately, the Confirm method doesn t offer a single OK button choice to provide a more compact version of an alert.
Figure 15-4: Clicking Yes in the first (upper) confirm message box displays the lower one.
Changing data source field values or adding and deleting repeating form sections triggers the following three events:
Adding field event handlers requires a data source and controls to change values. Follow these steps to convert the data source of your form to a simple XML document with fields of multiple data types, and add controls to the form s layout table.
Add a data source and controls to the form
True/False (Boolean) fields are a good choice for an initial OnAfterChange event- handler coding exercise, because the code is very simple. The event handler updates the value of the Boolean 2 text box with a value that corresponds to the state of the Boolean 1 check box. You specify the target node ”an IXMLDOMNode object ”by applying the thisXDocument.DOM.selectSingleNode("/eventsData/boolean2") method and then setting the target node s text property value to the text property value of the event handler s e.Source parameter. The code detects the e.operation = Delete condition and exits, so only Insert operations are processed .
It s a good programming practice to add structured exception handling to any event handler that might generate a run-time error, because InfoPath projects disregard some run-time exceptions. In this case, your code fails silently, and you might overlook an occasional or even a repetitive exception. Adding a stack trace to the error handler s message supplies the line number on which the error occurred.
Follow these steps to complete the boolean1 event handler with a Try...Catch...End Try block for exception handling.
Add an event handler for the boolean2 field
If e.Operation = "Delete" Then Return End If Try Dim nodTarget As IXMLDOMNode = _ thisXDocument.DOM.selectSingleNode("/eventsData/boolean2") nodTarget.text = e.Source.text Catch excBool As Exception thisXDocument.UI.Alert(excBool.Message + _ excBool.StackTrace.ToString) End Try
Nillable Date ( date ) and Date And Time ( dateTime ) fields present a challenge when you need to convert the Variant data type (Object) of dates returned by the event handler s e.Source.nodeValue property to the DateTime data type, which enables easy ISO 8601 formatting with the ToString( s ) method and date arithmetic. To avoid run-time errors, you must test for e.Source.text values that result from entering empty date values in the date picker s text box.
To create the date1_OnAfterChange event handler, follow these steps.
Add an event handler for the date1 field
If e.Operation = "Delete" Then Return End If 'Specify date1 as the field to be updated Dim nodTarget As IXMLDOMNode = _ thisXDocument.DOM.selectSingleNode("/eventsData/date2") If e.Source.text = "" Then 'Date is nil nodTarget.text = "" Try 'Format the DateTime object as ISO 8601 Dim strDate As String = _ CType(e.Source.nodeValue(), DateTime).ToString("s") 'Remove the time value nodTarget.text = Left(strDate, InStr(strDate, "T") - 1) 'Select the eventsData node nodTarget = nodTarget.parentNode 'Set the attribute value nodTarget.attributes(0).text = "date1 Changed" Catch excDate As Exception 'Catch runtime errors thisXDocument.UI.Alert(excDate.Message + _ excDate.StackTrace.ToString) End Try
If you want to see an exception-handling alert, comment the second Return statement, add a date value, clear the date value, and press Tab. You ll receive two error messages similar to the one shown in Figure 15-5.
Figure 15-5: A common source of InfoPath event-handler exceptions is attempts to cast a nondate Variant value to the DateTime type, as illustrated by this alert.
The OnBeforeChange event lets you intercept changes, apply business logic to the proposed new value, let users decide whether to make the change with a confirm message box, send a ReturnMessage to an alert message box, or any combination of these actions. Your code must handle multiple events correctly to avoid reoccurring confirm message boxes. In this procedure, a blnHasChanges flag determines whether the value has been changed by a previous OnBeforeChange Insert event. This procedure s event- handler code isn t prone to run-time errors, so exception handling isn t implemented.
Follow these steps to add the dateTime1_BeforeChange event handler.
Add an OnBeforeChange event handler for the dateTime1 field
If e.Operation = "Delete" Then Return End If If blnHasChanged Then blnHasChanged = False Return End If If thisXDocument.UI.Confirm("Do you want to update " + _ "dateTime2 and dateTime1?", XdConfirmButtons.xdYesNo) = _ XdConfirmChoice.xdYes Then e.ReturnStatus = True True blnHasChanged = 'Cancel the update e.ReturnStatus = False blnHasChanged = False e.ReturnMessage = _ "DateTime updates were canceled by the user." End If
The following procedure displays ISO 8601 “formatted date and time values for local time or Universal Time Coordinate (UTC) in the Date Time 1 text box, depending on the state of the Boolean 2 check box. The code is similar to that for the date2_OnAfterChange event handler, except for addition of the boolean2 field value test and code to change local to UTC time, as shown here:
Dim datNodeValue As DateTime = _ CType(e.Source.nodeValue(), DateTime) Dim strNodeValue As String 'Get the value of the boolean2 node Dim nodIsUTC As IXMLDOMNode = _ thisXDocument.DOM.selectSingleNode("/eventsData/boolean2") If nodIsUTC.text = "true" Then 'Use Universal Coordinated Time strNodeValue = datNodeValue.ToUniversalTime.ToString("s") Else 'Use local time strNodeValue = datNodeValue.ToString("s") End If
To add the dateTime1_OnAfterChange event handler, follow these steps.
Add an OnAfterChange event handler for the dateTime1 field
Creating a new InfoPath project from an existing template is a simple, two-step process if your form doesnt incorporate JScript or VBScript code. Moving to managed code disables the script, and you must rewrite all event handlers in Visual Basic .NET or C#. If your template contains more than 100 lines of script, conversion probably isnt justified unless you need to implement features that the .NET Framework supports and script doesnt. As an example, you need the .NET Framework and the Microsoft Web Services Enhancements (WSE) 2.0- add-on to connect to secure Web services that implement SOAP message encryption and require digital signatures. Digitally signed SOAP messages supplementbut dont replacedigital signatures applied to forms.
RSS 2.0 data documents have a repeating item section with a pubDate field that requires a RFC1123-formatted date string. Repeating sections introduce you to the use of the IXMLDOMNodeList object, which contains a collection of repeating IXMLDOMNode elements. The objects length property returns the number of instances of the repeating section. You can specify the instance you need by an index thats less than the length valuefor example, NodeList1(NodeList1.length -1) points to the last section.
Adding the current time in RFC1123 format when inserting a new section improves data entry efficiency and minimizes typographic errors. The DateTime.UtcNow property returns the current UTC time, and the ToString(R) method returns the RFC1123 pattern. Its a common practice to add RSS 2.0 items in reverse date/time order (last in, first out) with Insert Above operations, but users can insert new sections in any arbitrary sequence. Thus your code must test all sections for the presence of a pubDate field value and add the current date/time value if its missing.
To add the RFC1123 date and time to a copy of the Rss2v4.xsn template from Chapter 10, Adding Views to a Template, follow these steps.
Create the Rss2v4Events project and add the event-handling code
If e.Operation = "Delete" Or e.Site.nodeName <> "item" Then Return End If Try Dim strXML As String = e.Source.xml 'Create a list of pubDate node(s) Dim lstItems As IXMLDOMNodeList = _ e.Source.selectNodes("//pubDate") 'Check all pubDate nodes Dim intItem As Integer For intItem = 0 To lstItems.length - 1 Dim nodLastItem As IXMLDOMNode = lstItems(intItem) nodLastItem.selectSingleNode("//pubDate") If nodLastItem.text = "" Then 'Get current system time as UTC if pubDate is empty Dim datNow As DateTime = DateTime.UtcNow 'Add the RFC1123 date nodLastItem.text = datNow.ToString("R") End If Next intItem Catch excInsert As Exception thisXDocument.UI.Alert(excInsert.Message + _ excInsert.StackTrace) End Try
The e.Site.nodeName <> "item" test in the first statement verifies that the event originated from inserting an item node and not from the change to the pubDate field value. Events lower in the XML documents hierarchy percolate to the root element, a process called event bubbling . For more information about the Site object and event bubbling, search the InfoPath SDK Documentation for the term bubbling (include the quotation marks).
The following sections show you how to handle errors that occur when you open a form from a project template in a location other than the ProjectName folder, change the location of the InfoPath project files, create a release version of your template, and publish production templates.
You can create and save a sample data document from the Fill Out A Form task pane in the project s InfoPath design window. In this case, InfoPath use the manifest.xsf file in the project folder as the template and adds it to the local form template cache. Alternatively, you can emulate opening a production Form1 by double-clicking the template file that contains the project s assembly DLL ”IPEvents.xsn for this example ”in the ... ProjectName inDebug folder. Opening Form1 from this folder displays the Form Conflict dialog box shown in Figure 15-6. If you re developing the form template, click the Keep Form On Your Computer button to use the manifest.xsf version. After you complete the form s design, click the Replace Form On Your Computer button to use the FormName.xsn template.
Figure 15-6: This message box appears when you attempt to open a new Form1 from the template s .xsn file in the ...inDebug folder and the manifest.xsf version of the template is in the local cache.
Visual Studio users appreciate easy XCopy deployment of their Windows and Web forms projects; moving an entire InfoPath project to a new folder is as easy as using Save As with a conventional template. You can copy the entire project folder to another location and continue development without making any changes to the project. When you open the project, you might receive a warning message that advises you to publish the form from its new location. You can ignore the warning message prior to creating a release version of the project and publishing the template for production use.
By default, Visual Studio creates and runs InfoPath projects in debug configuration. The debug version of the assembly ( ProjectName .dll) is slightly larger than the release version, and debug versions of large projects execute more slowly than release versions. The debug symbols (.pdb) file adds unnecessary bulk to the template file. Before you publish a production form, create the release version by choosing Build, Configuration Manager to open the Configuration Manager dialog box. Select Release in the Active Solution Configuration drop-down list, as shown in Figure 15-7, and click OK.
Figure 15-7: Changing the InfoPath project s configuration from Debug to Release generates a production version of the project without a ProjectName .pdb file.
Run the project to create a ... ProjectName inRelease folder, which contains release versions of your template and its assembly. The completed projects in your C:Microsoft PressIntroducing InfoPath 2003Chapter15IPEventsIPEvents and ...Rss2v4EventsRss2v4Events folders have debug and release versions. Debug is the default configuration for these projects.
The process of publishing an InfoPath template with managed code to shared folders, Web sites, and SharePoint forms libraries is identical to publishing a template with or without script. Choose Tools, Publish Form to build the form and start the Publishing Wizard. Complete the wizard s steps, as described in Chapter 12, Publishing Form Templates. The template file contains the assembly DLL. InfoPath publishes your template s release version, if it exists; if not, InfoPath publishes the debug version.
Like forms containing script or ActiveX controls, Domain is the default security mode for forms containing managed code. If your code accesses local computer resources outside of the form, requires cross-domain permissions, or includes references to custom .NET assemblies that aren t signed by Microsoft, you must specify Full Trust as the security mode and specify a code signing certificate to digitally sign the form on the Security tab of the Form Options dialog box. Alternatively, you can deploy a custom-installed template to users computers.
InfoPath projects substitute .NET managed code for script to implement business logic that declarative programming cant handle. You must have Visual Basic .NET 2003 Standard or Visual Studio .NET 2003 Professional or later and the Toolkit for InfoPath 2003 installed to create InfoPath projects with Visual Basic .NET code. This book uses Visual Basic .NET for all programming examples, because Office developers can leverage their VBA skills to write InfoPath event handlers and utility functions or subprocedures.
The Toolkit creates an event-handling subprocedure stub with default code when you specify an event handler by choosing Tools, Programming. Clicking Edit with an event selected on the Validation And Event Handlers tab of a field or sections Properties dialog box also generates a stub. You add code to implement business logic to the stub to complete the event handler. Adding structured exception handling to the code you write ensures that you catch run-time errors that otherwise might not be evident.
Debug is Visual Studios default configuration for InfoPath projects. To publish a production InfoPath project, you use the Configuration Manager to change to Release configuration, and rebuild your project. You publish the project by choosing Tools, Publish Form in Visual Studio to run InfoPaths Publishing Wizard. The resulting .xsn file includes the .NET assembly that contains the projects MSIL code. Users must have the .NET Framework 1.1 runtime installed on their computers to JIT-compile the MSIL to executable code.
Does InfoPath trigger more than the three events discussed in this chapter?
Why do InfoPath projects require programming MSXML objects rather than .NET s more versatile System.Xml classes?
Can I use .NET s System.Xml classes to manipulate the data document independent of InfoPath?
Are there any other InfoPath UI objects that I can program with .NET code?
Yes, but OnBeforeChange , OnValidate , and OnAfterChange are the most frequently used event handlers in InfoPath projects. The Programming option on InfoPath s Tools menu provides On Load Event, On Switch View Event, On Context Change Event, and On Sign Event items, which generate the corresponding event handlers. The OnSubmitRequest and OnVersionUpgrade events let you write code to customize the form submission and upgrade processes. SP-1 adds several more events to InfoPath s repertoire ; the next two chapters have code examples for the events not covered in this chapter.
InfoPath 2003 SP-1 is a conventional Windows application that exposes COM objects to provide access to Application and XDocument objects. You re limited to manipulating InfoPath COM objects that managed code accesses through the InfoPath PIA. InfoPath, not MSXML, provides the IXMLDOM interfaces that you program with .NET code. SP-1 exposes its classes and interfaces as Automation objects, so Visual Basic 6 or VBA code can manipulate InfoPath objects by adding a reference to the Microsoft InfoPath 1.0 Type Library.
Yes, you can retrieve the cached XML document s contents as a String at any point in the editing process with a thisXDocument.DOM.xml instruction. After you add a reference to System.Xml.dll and an Imports System.Xml statement to InfoPath s default FormCode.vb, you can populate an in-memory XmlDocument instance with a docXML.LoadXml(thisXDocument.DOM.xml) statement. You can modify docXML , navigate it with an XPathNavigator object, import it to a DataSet (if the document is relational), and transform it with Extensible Stylesheet Language Transformation (XSLT). Your form must be fully trusted to save the modified document to an .xml file with the XmlTextReader class.
Yes. The XDocument.UI object provides a ShowModalDialog method that displays a dialog box that you define using a conventional HTML file, which can contain script. The form must be fully trusted to open the dialog box. The InfoPath 2003 SDK s documentation offers additional implementation details for the method; type showmodaldialog as the search term and follow the links. You also can display the e-mail message and signature dialog boxes with the ShowMailItem and ShowSignatureDialog methods .
Heres an additional exercise that uses the Rss2v4Events.xsn template to demonstrate event-handling code reuse:
Part I - Introducing Microsoft Office InfoPath 2003 SP-1
Part II - Designing InfoPath Forms
Part III - Working with Databases and Web Services
Part IV - Programming InfoPath Forms