Microsoft InfoPath 2010
The official blog of the Microsoft InfoPath team

  • Microsoft InfoPath 2010

    Problems When Calling into Script Functions in a Task Pane from Managed Code


    We have been receiving questions about problems deploying some InfoPath solutions created with the InfoPath 2003 Toolkit that call into script functions in a task pane from managed code. These solutions work on the developer's machine, but then fail when deployed on user's machines if the MSHTML primary interop assembly (PIA) - microsoft.mshtml.dll - is missing.

    This problem is caused by InfoPath object model members that return types from the MSHTML PIA. The MSHTML PIA is installed by default with Visual Studio .NET 2003, but is not installed by InfoPath 2003 SP1 or with the .NET Framework. As a result, when the solution is deployed on a machine missing the MSHTML PIA, the solution throws an exception.

    There are two ways to resolve this issue: 1) Install and register the MSHTML PIA on the user's machine, or 2) Use late binding in your form code to call such functions.

    Installing the MSHTML PIA on User's Machines

    The MSHTML PIA is a redistributable component installed with Visual Studio .NET 2003 (as defined in the <drive>:\Program Files\Microsoft Visual Studio .NET 2003\redist.txt file), so you can deploy it on all client machines where you want the solution to run. Refer to the End User License Agreement (EULA) for your edition of Visual Studio .NET 2003 for terms and conditions on redistributing components.

    The MSHTML PIA (microsoft.mshtml.dll) needs to be copied to the user's machine, installed in the Global Assembly Cache (GAC) using the Global Assembly Cache tool (gacutil /i microsoft.mshtml.dll), and then registered using the Assembly Registration Tool (regasm microsoft.mshtml.dll).

    Using Late Binding to Call Script Functions From Managed Code

    You may also consider using late-bound calls to objects previously passed by the task pane script to the managed code in your form code. The following is a code fragment that shows how to do this by using the InvokeMember method of the System.Type class in the .NET Framework. Note that the managed code requires FullTrust permissions to work with late-bound calls. For more information on FullTrust permissions and how to deploy form templates that require this permission set, see Understanding Fully Trusted Forms.


       function Initialize()
          var XDoc = window.external.XDocument;

       function MyScriptFunc(s)

    <BODY onload="Initialize();" />
       // Remaining taskpane HTML goes here.


    private XDocument      thisXDocument;
    private Application    thisApplication;
    private object         taskPaneWindow;

    // Note: The MatchPath attribute should match your control's ID.
    [InfoPathEventHandler(MatchPath="CTRL1_5", EventType=InfoPathEventType.OnClick)]
       public void CTRL1_5_OnClick(DocActionEvent e)
          object[] args =  new object[] {"Hello from InfoPath script"};

          // Call into script through CLR late binding using the InvokeMember method.
             "MyScriptFunc",              // late-bound method
             BindingFlags.InvokeMethod |  // binding flags
             BindingFlags.DeclaredOnly |
             BindingFlags.Public |
             null,                        // binder object
             taskPaneWindow,              // target object

       public void SetTaskPaneWindow(object window)
          taskPaneWindow = window;

  • Microsoft InfoPath 2010

    Enforcing unique values in a repeating list

    Have you ever created a form which allows the user to choose items from a list and you wanted to make sure the user doesn't choose the same item twice?  If you've got InfoPath 2007 you can use the new Multi Select List Box, but if you've got InfoPath 2003, you're still in luck, this blog entry is for you!
    Note: You should be familiar with XPath expressions before preceding.
    Let's start with a repeating table with a dropdown control bound to a secondary data source.
    There are a few choices when it comes to enforcing unique values selected by the user:
    1. Only show values that have not already been selected by the user.
    2. Show a validation error when the user selects something which has already been selected.
    3. Write code using the Changing event.
    This blog entry will cover both options 1 and 2.
    Option 1 - Only show values that have not already been selected by the user
    Only showing certain values implies that a filter is being applied to the dropdown.  To apply a filter, click the Filter Data… button when selecting the entries for the dropdown to show the condition builder.  Unfortunately, there is no UI in the condition builder to build an expression that means "don't show anything that is already in the list".  Thus, we'll have to construct this manually by selecting The Expression.
    You might be tempted to put the following expression:
    . != xdXDocument:get-DOM()/my:myFields/my:items/my:item/my:product
    This will return true if any one value from the list matches the current value.  In other words, this condition will always return true if there are two entries in the list which are different.  (Definitely not what we are looking for)
    The correct condition is a slight adjustment to the former expression:
    not(. = xdXDocument:get-DOM()/my:myFields/my:items/my:item/my:product)
    This will only return true if none of the values from the list matches the current value.  In other words, only values that are not in the list will be displayed.
    Preview the form and you will see that the dropdown only lists the products that have not already been selected.
    Option 2 - Show a validation error when the user selects something which has already been selected
    The only way this will work properly is if we add a hidden calculated field along with the product selection.
    (The reason for this is a bit too complex to explain here, so it'll have to wait for another time.)
    The formula for the unique field is the following:
    not(../my:product = (../preceding-sibling::my:item | ../following-sibling::my:item)/my:product)
    This will set Unique to true if and only if the product isn't already selected preceding or following the current item.
    The next and final step is to use data validation on the dropdown to show an error.
    Preview the form and you will see that the dropdown will show a validation error if the items are not unique.
    - Gary
    Software Development Engineer
  • Microsoft InfoPath 2010

    Having more control over Page Breaks using Conditional Formatting

    When printing forms, sometimes it might be desired to have more control over where Page Breaks should occur. For example, you might want a Repeating Table to start printing on a new page once it has reached a certain number of rows or you might wish to provide a way for users who are filling out forms to be able to insert/remove Page Breaks in certain locations of the form. You can accomplish this using Page Breaks (available with SP1) with Conditional Formatting.
    Example 1: Allow users to insert a Page Break when a Checkbox is checked
    1. In Design mode, insert a Checkbox Control, type "Insert Page Break" next to the checkbox.
    2. Insert a Section Control (From the Control Task Pane, click on "Section")
    3. Within the Section Control, insert a Page Break (Place the cursor inside of the Section Control you just inserted and from the menu Insert | Page Break)
    4. Double click on the Section Control to open the Section Properties dialog. Select the Display tab and click on the Conditional Formatting button.
    5. Click  Add... to set a new condition, which will keep the Page Break "hidden" unless the checkbox is checked, to do this: Select the name of the checkbox in the first condition drop-down and set the condition "is equal to FALSE". Check the "Hide this Control" checkbox and close all open dialogs by clicking "Ok"
    6. You can test your form by clicking on the Preview Form button, in the toolbar.
    Example 2: Start printing a Repeating Table in a new page once it has more than 3 rows
    1. In Design mode, insert a Section Control (From the Control Task Pane, click on "Section")
    2. Within the Section Control, insert a Page Break (Place the cursor inside of the Section Control you just inserted and from the menu Insert | Page Break)
    3. Place the mouse cursor beneath the Section Control and insert a Repeating Table (From the Control Task Pane, click on Repeating Table).
    4. Double click on the Section Control to open the Section Properties dialog. Select the Display tab and click on the Conditional Formatting button.
    5. Click Add... to set a new condition, which will keep the Page Break "hidden" unless the number of rows in the "Repeating Section" exceeds 3, to do this: Select  "The expression" in the first condition drop-down and type the following XPath expression referring to the Repeating Table, "count(../my:group2/my:group3) <=3" in the text field next to it. Check the "Hide this Control" checkbox and close all open dialogs by clicking "Ok"
    6. You can test your form by clicking on the Preview Form button, in the toolbar.
  • Microsoft InfoPath 2010

    Do You Love Access? We do too!


    And that's why there's all the new-and-cool documentation about how to make your InfoPath forms work well with Access:

    And a few bonus articles, also from our friends in the documentation team:

    Happy Friday!

  • Microsoft InfoPath 2010

    Open your rolodex from InfoPath using the Contact Selector

    Speaking of Outlook integration, Microsoft Office 2007 includes a new control that enables you to choose one or more e-mail address from the address book.
    You can add this control to InfoPath’s list of custom controls by using the Add or Remove Custom Controls wizard like you would for any other ActiveX control.  When registering the control, you can choose most of the default options. However, you must choose "Field or Group (any data type)" in the "Specify Data Type Options" page of the wizard. If you don't choose this binding type, the control will be disabled when you try to fill out the form.
    When you insert this control in a form template, it looks like this:
    When you insert this control in design mode, only a group node is added to the data source. You must manually add additional fields in order for this control to work correctly. To determine the final data structure should be, open the properties dialog for the control and click on the "Items" tab. This tab lists the schema structure that is required.
    As I mentioned, you must manually add these fields to the data source. This can be quite painful if you want to use the control in a lot of form templates. However, here’s a little trick.  You can create a Template Part that contains only the data structure and no controls in the view. Now, when you need to use the Contact Selector, first insert the Template Part to create the data structure.  (This will insert an empty Section control into the view which you can then remove.)  Then, go to the data source, right-click on the group node that was inserted, and select Contact Selector from the More menu.
    Template Parts don't support ActiveX controls so you can't insert the Contact Selector into the template part which would make this even easier. However, using this empty template part that contains only the data structure is still easier than creating the structure from scratch each time.
    Scott Roberts
    Senior Development Lead
  • Microsoft InfoPath 2010

    Debugging Sandboxed Code in InfoPath 2010 Forms


    In this video demo, Phil Newman from the InfoPath program management team shares some tips and tricks for debugging InfoPath forms with sandboxed code on SharePoint server 2010.

    Get Microsoft Silverlight

    Here is this link to the ULS viewer tool that is used in the demo:


    • Process=SPUCWorkerProcess (for administrator-approved forms, filter on the W3WP process)
    • Message=Exception thrown from business logic
  • Microsoft InfoPath 2010

    Introducing Horizontal Repeating Tables

    InfoPath 2007 adds horizontal repeating table to the control toolbox for when you want data to be entered or displayed in a structured, tabular format, and when you want users to be able to add additional columns instead of rows.
    For example, you might use a horizontal repeating table to collect sales data for each quarter:
    Making it wrap
    When designing your form template, horizontal repeating table looks like a composite control -- it’s made from a horizontal repeating section and layout tables (I'll cover horizontal repeating section in my next post). Horizontal repeating table can be configured to wrap or scroll. By default, it will wrap to the next line, to provide a better printing experience.
    You can change the behavior of the control by resizing the layout tables; for example, to change the wrapping point, resize the rightmost border of the layout table:
    This will allow only two instances of the repeating column to fit on one line; the next two will go to the next line, and so forth:
    Making it scroll
    You may want to set an upper bound on the amount of real estate that a horizontal repeating table can take up; in that case, you need to make the control scroll. User experience will then be similar to the following:
    To achieve such behavior, you need to make the following modifications to your form template:
    1. Encapsulate the repeating section component of the horizontal repeating table in a scrolling region.
    2. Set the properties on the scrolling region:
      • Show horizontal scroll bars: always
      • Show vertical scroll bars: never
      • Wrap text: unset
      • Margins: all zero
      • Padding: all zero
      • Size: appropriate height and width
      • Borders: no borders
    3. Resize the rightmost border of the layout table that contains the horizontal repeating table to match the border of the scrolling region.
    When you’re done, your form template will look similar to the following in Design mode:
    Scrolling is often desirable for electronic forms, but is not ideal for printing. If you want to achieve both, you need to use scrolling in the view that users will see when filling out the form and wrapping in an associated printable view.
    Data binding of a horizontal repeating table is identical to that of a regular repeating table or section.  Horizontal repeating tables support conditional formatting, data validation, rules, calculations, other InfoPath data features that a regular repeating tables support.
    Note that horizontal repeating tables are only supported in InfoPath 2007 client.
    - Alex Weinstein
    Program Manager
  • Microsoft InfoPath 2010

    TechEd Source Code


    Some people were using digital cameras to capture the source code from our "InfoPath: Developing Forms with Managed Code" sessions, but we want to make it easier than that!

    So here's the code Ned Friend used in the TechEd Europe version of the presentation (David Gerhardt used similar code in his USA version of the talk in Orlando):

    DISCLAIMER: This code is designed to be short and sweet for demo purposes only. IT IS NOT BEST PRACTICES NOR READY FOR PRODUCTION.

    using System;

    using Microsoft.Office.Interop.InfoPath.SemiTrust;

    // Add .NET reference: System.Xml

    using System.Xml;

    using System.Xml.Xsl;

    using System.Security.Cryptography;

    using System.IO;

    // Add COM reference: Microsoft Word 11.0 Object Library

    using Word = Microsoft.Office.Interop.Word;


    // Office integration attribute. Identifies the startup class for the form. Do not

    // modify.

    [assembly: System.ComponentModel.DescriptionAttribute("InfoPathStartupClass, Version=1.0, Class=StatusReport.StatusReport")]

    namespace StatusReport


    // The namespace prefixes defined in this attribute must remain synchronized with

    // those in the form definition file (.xsf).

        [InfoPathNamespace("xmlns:soapenc=\"\" xmlns:xdUtil=\"\" xmlns:xdXDocument=\"\" xmlns:tm=\"\" xmlns:dfs=\"\" xmlns:xhtml=\"\" xmlns:xd=\"\" xmlns:soap=\"\" xmlns:tns=\"\" xmlns:msxsl=\"urn:schemas-microsoft-com:xslt\" xmlns:http=\"\" xmlns:xdMath=\"\" xmlns:xsf=\"\" xmlns:mime=\"\" xmlns:wsdl=\"\" xmlns:my=\"\" xmlns:xdDate=\"\" xmlns:xsi=\"\"")]

        public class StatusReport


            private XDocument thisXDocument;

            private Application thisApplication;

            // For encryption/decryption

            private Rijndael key = new RijndaelManaged();

            public void _Startup(Application app, XDocument doc)


                thisXDocument = doc;

                thisApplication = app;

                // You can add additional initialization code here.


            public void _Shutdown()



            // The following function handler is created by Microsoft Office InfoPath. Do not

            // modify the type or number of arguments.

            [InfoPathEventHandler(EventType = InfoPathEventType.OnLoad)]

            public void OnLoad(DocReturnEvent e)


                // Set the username

                IXMLDOMNode userName =


                if (userName.text == String.Empty)


                    userName.text = Environment.UserName;


                // Load key information

                IXMLDOMNode keyNode = thisXDocument.DOM.selectSingleNode("/my:status/my:key");

                IXMLDOMNode ivNode = thisXDocument.DOM.selectSingleNode("/my:status/my:iv");

                byte[] keyBytes = Convert.FromBase64String(keyNode.text);

                byte[] ivBytes = Convert.FromBase64String(ivNode.text);

                // Decrypting the project name

                MemoryStream ms = new MemoryStream();

                CryptoStream csRijndael =

                    new CryptoStream(ms, key.CreateDecryptor(keyBytes, ivBytes), CryptoStreamMode.Write);

                IXMLDOMNode projectNode =


                byte[] projectBytes = Convert.FromBase64String(projectNode.text);

                csRijndael.Write(projectBytes, 0, (int)projectBytes.Length);


                string projectName =

                    System.Text.Encoding.Unicode.GetString(ms.GetBuffer(), 0, (int)ms.Length);

                projectNode.text = projectName;


            // The following function handler is created by Microsoft Office InfoPath. Do not

            // modify the type or number of arguments.

            [InfoPathEventHandler(MatchPath = "/my:status/my:date", EventType = InfoPathEventType.OnValidate)]

            public void date_OnValidate(DataDOMEvent e)


                // Custom data validation because InfoPath does not have functions for date arithmetic

                if (e.Operation == "Insert")




                        DateTime date = DateTime.Parse(e.NewValue.ToString());

                        if (date.CompareTo(DateTime.Today.AddDays(5)) > 0)


                            e.ReportError(e.Site, "Date cannot be more than 5 days away.",

                                                         false, null, 1, "modal");



                    catch (FormatException)


                        // Incorrectly formatted dates are handled automatically by InfoPath.




            // The following function handler is created by Microsoft Office InfoPath. Do not

            // modify the type or number of arguments.

            [InfoPathEventHandler(EventType = InfoPathEventType.OnSubmitRequest)]

            public void OnSubmitRequest(DocReturnEvent e)


                // If Online, submit using data adapter

                if (thisApplication.MachineOnlineState == XdMachineOnlineState.xdOnline)


                    WebServiceAdapter2 webServiceAdapter =




                // If Offline, save to cache



                    XmlDocument xmlDoc = new XmlDocument();

                    string now = DateTime.Now.ToString("yyyy-MM-dd");

                    xmlDoc.PreserveWhitespace = true;


                    xmlDoc.Save("C:\\TechEd2005\\Submit\\Form " + now + ".xml");


                e.ReturnStatus = true;


            // The following function handler is created by Microsoft Office InfoPath. Do not

            // modify the type or number of arguments.

            [InfoPathEventHandler(EventType = InfoPathEventType.OnSaveRequest)]

            public void OnSaveRequest(SaveEvent e)


                // Set up encryption streams

                MemoryStream ms = new MemoryStream();

                CryptoStream csBase64 =

                    new CryptoStream(ms, new ToBase64Transform(), CryptoStreamMode.Write);

                CryptoStream csRijndael =

                    new CryptoStream(csBase64, key.CreateEncryptor(), CryptoStreamMode.Write);

                // Encrypt project name

                IXMLDOMNode projectNode =


                byte[] projectBytes =


                csRijndael.Write(projectBytes, 0, (int)projectBytes.Length);


                string projectEncrypted =

                    System.Text.Encoding.ASCII.GetString(ms.GetBuffer(), 0, (int)ms.Length);

                // Save key information

                IXMLDOMNode keyNode = thisXDocument.DOM.selectSingleNode("/my:status/my:key");

                IXMLDOMNode ivNode = thisXDocument.DOM.selectSingleNode("/my:status/my:iv");

                keyNode.text = Convert.ToBase64String(key.Key);

                ivNode.text = Convert.ToBase64String(key.IV);

                // Save encrypted project name, then decrypt in view

                string projectOriginal = projectNode.text;

                projectNode.text = projectEncrypted;

                e.IsCancelled = e.PerformSaveOperation();

                projectNode.text = projectOriginal;


                e.ReturnStatus = true;


            // The following function handler is created by Microsoft Office InfoPath. Do not

            // modify the type or number of arguments.

            [InfoPathEventHandler(MatchPath = "ButtonGenerate", EventType = InfoPathEventType.OnClick)]

            public void ButtonGenerate_OnClick(DocActionEvent e)


                // This button converts the XML Form to a Word Document


                // Load the XSLT file from resources

                IXMLDOMDocument domDocument = thisXDocument.CreateDOM();


                XmlDocument xsltDocument = new XmlDocument();


                // Load the XML DOM into System.Xml

                XmlDocument infoPathDocument = new XmlDocument();


                // Apply the XSLT to the XML DOM

                XslCompiledTransform xslt = new XslCompiledTransform();


                XmlDocument outputDocument = new XmlDocument();

                System.Xml.XPath.XPathNavigator outputNavigator = outputDocument.CreateNavigator();

                using (XmlWriter writer = outputNavigator.AppendChild())


                    xslt.Transform(infoPathDocument, writer);


                // Instantiate Word with the new document

                object missing = System.Reflection.Missing.Value;

                Word.Application wordApplication = new Word.ApplicationClass();

                Word.Document oDoc = new Word.DocumentClass();

                oDoc = wordApplication.Documents.Add(ref missing, ref missing, ref missing, ref missing);

                wordApplication.Selection.Range.InsertXML(outputDocument.OuterXml, ref missing);

                wordApplication.Visible = true;




  • Microsoft InfoPath 2010

    Using the Events Manager of the InfoPath Hosted Control


    The InfoPath hosted control gives developers of third party hosted applications the ability to respond to events in the form.  The InfoPath event manager provides this functionality.  Through the event manager a host application can respond to 5 form events, 3 xml events and 1 control event:

    Form Events

    Xml Events

    Control Events

    Saving event
    Context Changed event
    Sign event
    Merging event
    View Switched event

    Changing event
    Validating event
    Changed event

    Clicking event

    How can third party host applications use the event manager?

    Step 1: Implement an InternalStartup method

    First, add code to handle the InternalStartup event. InternalStartup method will execute when a form loads.

    public Form1()
                //sync to the startup event where you can register for individual events
                formControl1.InternalStartup += new Microsoft.Office.InfoPath.FormControl.EventHandler<EventArgs>(InternalStartup);

    Next, implement your InternalStartup method. The function signature should look similar to:

    void formControl1_InternalStartup(object sender, EventArgs e)


    Step 2: Register to receive events

    In your InternalStartup method add code to register for events. For form events this code looks like this.

    void InternalStartup(object sender, EventArgs e)
           ((FormControl)sender).EventManager.FormEvents.ViewSwitched += new ViewSwitchedEventHandler(OnSwitchView);
           ((FormControl)sender).EventManager.FormEvents.Submit       += new SubmitEventHandler(OnSubmit);
           ((FormControl)sender).EventManager.FormEvents.Sign         += new SignEventHandler(OnSign);
           ((FormControl)sender).EventManager.FormEvents.Save         += new SaveEventHandler(OnSave);
           ((FormControl)sender).EventManager.FormEvents.Merge        += new MergeEventHandler(OnMerge);

    For xml events you must provide the XPath of the node whose events you wish to respond to. Below is a sample of how to register to receive xml events.


    void InternalStartup(object sender, EventArgs e)
          ((FormControl)sender).EventManager.XmlEvents["/my:myFields/my:field1"].Changed    += new XmlChangedEventHandler(FieldChanged);
          ((FormControl)sender).EventManager.XmlEvents["/my:myFields/my:field1"].Changing   += new XmlChangingEventHandler(FieldChanging);
          ((FormControl)sender).EventManager.XmlEvents["/my:myFields/my:field1"].Validating += new XmlValidatingEventHandler(FieldValidating);

    To receive the click event for a button you must specify the control id of the button. Below is a sample of how to register to receive control events.

    void InternalStartup(object sender, EventArgs e)
          ((ButtonEvent)((FormControl)sender).EventManager.ControlEvents["CTRL2_5"]).Clicked += new ClickedEventHandler(ButtonClicked);


    Step 3: Implement methods to handle each event registered

    The final step is to implement handlers for the events you have registered for.
    The handlers for the events have the following method signatures.

    Form Events

    public delegate void ViewSwitchedEventHandler(object sender, Microsoft.Office.InfoPath.ViewSwitchedEventArgs e)
    public delegate void SubmitEventHandler(object sender, Microsoft.Office.InfoPath.SubmitEventArgs e)
    public delegate void SignEventHandler(object sender, Microsoft.Office.InfoPath.SignEventArgs e)
    public delegate void SaveEventHandler(object sender, Microsoft.Office.InfoPath.SaveEventArgs e)
    public delegate void MergeEventHandler(object sender, Microsoft.Office.InfoPath.MergeEventArgs e)

    Xml Events

    public delegate void XmlChangedEventHandler(object sender, Microsoft.Office.InfoPath.XmlEventArgs e)
    public delegate void XmlChangingEventHandler(object sender, Microsoft.Office.InfoPath.XmlChangingEventArgs e)
    public delegate void XmlValidatingEventHandler(object sender, Microsoft.Office.InfoPath.XmlValidatingEventArgs e)

    Control Events

    public delegate void ClickedEventHandler(object sender, Microsoft.Office.InfoPath.ClickedEventArgs e)

    Example: changed event, view switched event, button clicked event.

    void FieldChanged(object sender, XmlEventArgs e) {}
    void OnSwitchView(object sender, ViewSwitchedEventArgs e) {}
    void button1_Click(object sender, EventArgs e) {}


    Things to know about handling events

    1. Events are handled first by the form’s business logic, then by others who register to handle events. Since events are handled asynchronously it is possible that the code in the business logic may cancel the event, and deny the host the opportunity to handle the event.
    2. In order to handle the submit, save and merging events, the designer of the form template must specify that the event is to be handled through the use of code. Without enabling this in the form template the event will not be handled in code.
    3. Buttons must specify that clicks be handled through code or the event will not be handled through code.
    4. The sign event will only take place when the form is fully trusted.
    5. The Loading and VersionUpgrade events are not available from third party hosted applications as these events occur before the form is loaded in the hosted application.

    DeVere Dyett
    Software Design Engineer in Test

  • Microsoft InfoPath 2010

    Create a Rating Control using Picture Buttons


    In this short video demo, Matt Bielich from the InfoPath test team shows how you can add a rating control to your InfoPath 2010 forms using picture buttons.

    Get Microsoft Silverlight
  • Microsoft InfoPath 2010

    Move Up/Move Down


    One stumbling block for developers coming up to speed on InfoPath is that the SDK documentation leads you to the edge of the XML cliff and then goes mysteriously quiet. For web developers who have programmed applications with the MSXML SDK for manipulating XML this isn’t a problem – they brought their parachute with them and happily jump off. For others, this can be a little intimidating.

    Here’s a good way to get started manipulating the XML DOM directly – let’s add “Move Up” / “Move Down” buttons to a Repeating Table so you can re-order the items.

    First off, let’s build the view:

    1. Fire up InfoPath 2003 and start a new blank form
    2. Insert a Repeating Table from the Controls task pane
    3. Delete the text box in the last column, and add two buttons in that cell instead
    4. Pull up the properties on first button and give it the label “5” and the ID “MoveUp”
    5. Pull up the properties on second button and give it the label “6” and the ID “MoveDown”

     At this point, you’re thinking “5 and 6???”

    1. Select both buttons (Click the first, and Ctrl+Click the second)
    2. On the Font drop-down on the toolbar, select Marlett. Then take a look at the buttons.

    (Sneaky, huh? Glyphs from this font are used by Windows to render all sorts of UI elements like the Minimize/Maximize Window buttons. This is a handy way to avoid having to use images for simple things like arrows.)

    Now that we have it looking pretty, let’s add the event handlers for the buttons. We’ll build on the logic from this blog entry to figure out which instance of the buttons was clicked.

    1. Pull up the properties on the first button and click Edit Code – paste the following into the editor:

    Sub MoveUp_OnClick(eventObj)

    ' Write your code here


       Dim oItem, oParent, oPrevious


       Set oItem     = eventObj.Source

       Set oParent   = oItem.parentNode

       Set oPrevious = oItem.previousSibling


       If Not ( oPrevious Is Nothing ) Then


              oParent.removeChild oItem


              oParent.insertBefore oItem, oPrevious


       End If


    End Sub

    The logic here is straightforward:

    • Grab the context node of the button (the row of the table, in our case)
    • If there is another item before the context node, remove the context node from the tree and re-insert it before the previous node
    1. Pull up the properties on the second button and click Edit Code – paste the following into the editor:

    Sub MoveDown_OnClick(eventObj)

    ' Write your code here


       Dim oItem, oParent, oNext


       Set oItem   = eventObj.Source

       Set oParent = oItem.parentNode

       Set oNext   = oItem.nextSibling


       If Not ( oNext Is Nothing ) Then


              oParent.removeChild oNext


              oParent.insertBefore oNext, oItem


       End If



    End Sub

    The logic here is similar:

    • Grab the context node of the button (the row of the table, in our case)
    • If there is another node after the context node, remove the it and re-insert it before the context node

    Almost done – let’s just use a little Conditional Formatting to disable the buttons when the item is the first or last in the list:

    1. Add a conditional for the first button which reads:

      if “The expression” “position() = 1” then Disable this control
    2. Add a conditional for the second button which reads:

      if “The expression” “position() = last()” then Disable this control

    Now try it out.

    As an aside, this was the author’s first foray into VBScript. Although I cut my teeth in Applesoft BASIC I’ve done most of my programming in C-style languages (C, C++, C#, Perl, Java). Did I do anything silly?

  • Microsoft InfoPath 2010

    Best Practices for Rules

    My forms have a bad habit of accumulating more and more rules as I add more and more functionality over time, so I came up with a set of guidelines to keep those rules clean and orderly.
    Remember that there are four places to add rules
    1. On Button Click (in Properties dialog)
    2. When values change (in Controls and Data Source Properties)
    3. On Submit (in Submitting Forms dialog)
    4. On Load (in Form Options | Open and Save section)
    Give your rules names for the scenario they capture
    Good names will summarize their conditions/actions in a more friendly way: eg "Submit using multiple data connections", or "Cancel query if expenses too high".
    Group rules by condition, but separate actions without conditions
    If more than one action has the same condition, put them together so you can edit the condition only once. Otherwise, put each action in its own rule, so you can easily reorder them independently.
    Use "Stop processing rules" to save retyping conditions
    If there's a case where you don't want to run any of your rules, add a rule that checks that case. It doesn't need to have any actions, just check the box to stop processing rules and put the rule before any of the other rules that would also check that case. For example, if you want to skip some submit logic if the expenses in the form are less than 100, put that condition in its own rule before your submit logic, and just have it stop the rules.
    Use dialog messages to debug while developing
    Dialog messages are great for tracing your rules at runtime, but are usually jarring while filling out the form. If you have a condition message to show, use conditional formatting instead to hide/show a section/expression-box with the message. You can even make the message dynamic by having the expression box look up a value in the data source and setting that value from the rule.
    Know when to use calculated default values vs set a field's value
    The “Set a field’s value” action is best for values that only need to be set/copied once, or for values that are conditional. For values that need to update every time another field changes unconditionally, best to use a calculated default value. Good examples for rules:
    • Demo button that populates an entire form with sample data
    • Copying suggested values from a secondary data source that the users can edit afterward
    • Setting someone’s email address based on their name, but only if the email field is blank beforehand
    Got tips?
    If you've got your own best practices we'd love to hear them! Post a comment to share your insights with the whole community.
    - Ned
  • Microsoft InfoPath 2010

    InfoPath and Yukon: The Details


    In a recent post, I touched upon the reasons why you might want to go with SQL Server XML columns as the storage for your InfoPath forms. In this article, we'll talk about actually making it work.

    InfoPath's strength is working with XML data sources: web services. In the implementation below, we'll write a simple web service that will help us retrieve and submit the data to Yukon.


    Build a resume database for the HR department. We want to store highly structured resumes in a database. Highly structured here is the opposite of freeform: a resume in our scenario isn't a blob of text; we have the graduation date, employment dates, actual titles and other things parsed into data structures.


    Data Structure

    We will store candidate records as XML blobs in a Yukon XML column. Each resume will be stored as a separate record; each record will also have a primary key - a JobCandidateID - to simplify our development. We'll define only one table, called JobCandidate:

    Populate the table with a few Resume XML files that all satisfy the same schema. Store the schema somewhere where the web server can access it.


    Core Implementation in English: we'll write a web service as a middle tier between SQL Server 2005 and the InfoPath form. This web service will have two methods:

    1) GetCandidateRecord: given a JobCandidateID (an integer), return a DataSet (for our purposes, an XSD-typed XML blob) that contains the candidate record. Give me the job candidate ID, I'll give you his resume.

    2) UpdateCandidateRecord: take a JobCandidateID (an integer) and a Resume (an XML document), and update the resume of the candidate with that particular ID to the resume passed in as a second parameter. Nothing fancy, really.


    Core Implementation in C#

    I promised you two methods, here they are. First, GetCandidateRecord.

    public DataSet GetCandidateRecord(int JobCandidateID)
        DataSet result = null;

        using (SqlConnection conn = new SqlConnection(connString))
            SqlCommand command = conn.CreateCommand();
            command.CommandText = @"
                SELECT Resume
                FROM "
    + tableName + @"
                WHERE JobCandidateID = @x"
            command.Parameters.Add("@x", SqlDbType.Int);
            command.Parameters[0].Value = JobCandidateID;
            SqlDataReader reader = command.ExecuteReader();

            if (reader.Read())
                DataSet ds = new DataSet();
                XmlDataDocument xd = new XmlDataDocument(ds);
                xd.Load(new StringReader((string)reader.GetValue(0)));
                result = xd.DataSet;
            return result;

    Things are fairly straightforward here:

    - Open a SqlConnection using ASP.NET credentials (make sure the ASPNET user has read/write rights to the database).

    - Build a simple SELECT statement to return a resume. Recall that the resume is just an XML document stored as-is in the database.

    - Cast the resume dataset into a typed dataset by applying a schema stored somewhere on the web server. Oh, I forgot to tell you - you need a schema :-). Why? InfoPath form needs to know what to expect from the web service, and while InfoPath can infer the shape of the data from the instance, this method is very much error prone. For example, how can InfoPath know of a repeating structure if only one instance was present in a sample XML document? How about choice or optional structures? Because of all of these reasons, you need to provide a typed dataset through your web service.

    - Return the typed dataset for the Resume record.


    Next, let's look at UpdateCandidateRecord.

    public void UpdateCandidateRecord(XmlDocument xml, int JobCandidateID)
        using (SqlConnection conn = new SqlConnection(connString))
            SqlCommand command = conn.CreateCommand();

            command.CommandText = @"
                UPDATE "
    + tableName + @"
                SET Resume = @x
                WHERE JobCandidateID = @y"
            command.Parameters.Add("@x", SqlDbType.Xml);
            command.Parameters[0].Value = xml.InnerXml.ToString();
            command.Parameters.Add("@y", SqlDbType.Int);
            command.Parameters[1].Value = JobCandidateID;

    - Open a SqlConnection

    - Build a simple UPDATE statement to save the resume for a given candidate. Note that you must use SqlCommand Parameters: just concatenating the XML blob won't do.

    - Execute the UPDATE statement. Note that we are replacing the entire resume with the new one; no partial updates are done. This means that simultaneous editing of Resume records won't be possible.


    Basic Form Template

    Now that the web service is set up, we can easily build a form template based on it. The template may or may not be browser-enabled; the method described here works with both. Just launch InfoPath designer, and pick "start from web service" as your data source. Specify GetCandidateRecord as the "receive" piece of the web service, and UpdateCandidateRecord as the submit part.

    InfoPath will ask you for sample JobCandidateID values for the receive web service; since our database already has a few Resumes, we can type in the JobCandidateID for one of them. You may be wondering - wait, I thought InfoPath won't do the schema inference by example! It won't - the dataset returned by your web service will contain a schema (that's why we called DataSet.ReadXmlSchema() in GetCandidateRecord), and InfoPath will use that schema to build your data source tree.

    After you've gone through the initial data connection setup, you'll notice that your main data source tree is correctly populated with the data types from your schema. Complex structures should show up just fine - repeating, optional, choice structures, non-string datatypes, XSD validation... And the Submit button should be configured to save the modified Resumes back to SQL Server.


    1. Why do we have to use a custom web service, and not built-in Yukon web services?
    There are unfortunate technical limitations that currently require you to write a custom web service to work with SQL Server 2005 in a manner described above. The web service is, as you saw, very easy; we know that this is something that can be made better, and will consider addressing this in future versions of InfoPath and SQL Server.

    2. Why not XSD-typed XML columns?
    When InfoPath submits datasets to the web service, it adds dataset tracking information; while you can add optional attributes to your InfoPath-generated schema and upload it to Yukon, this would complicate maintenance quite a bit.

    3. What other resources are available on the topic?
    Be sure to check out this article by S.Y.M. Wong-A-Ton.

    Alex Weinstein
    Program Manager

  • Microsoft InfoPath 2010

    Cool Forms! Weather Forecast Form


    This week’s cool form displays your local weather forecast by using a REST Web Service data connection to pull in weather information from an online weather service. There are two views to the form, one minimal and one extended. The form contains linked picture controls that use rules to concatenate the Web service data and generate a URL pointing to images on the weather site. By hosting this form inside the InfoPath form Web part, you can display the latest weather forecast information on your SharePoint portal pages.

    Minimal View:





    Extended View:


    For more information about using REST Web Service data connections with InfoPath forms, see our earlier blog post

    If you have a “cool” form that you would like to share with us, please send an e-mail with the following details to -

    • Attach 1 or 2 screenshots of your form
    • Provide a brief description of the form
    • You may also attach the XSN file (optional)

    The most popular submissions will be featured on our blog in future posts.

    Check out other Cool Forms here.

  • Microsoft InfoPath 2010

    Cool Forms! Feature Request Form


    This week’s cool form is a feature request issue tracking list used by the Visio team. It’s divided into two main sections, the separate status section allows readers to jump directly to the information they care most about. It uses picture buttons to allow selection between ‘Office 14’ and ‘Office 15’ and to show which one is currently selected.  This form also dynamically generates the proper direct link to the bug database based on the bug ID that is entered.

    Feature Request Form

    If you have a “cool” form that you would like to share with us, please send an e-mail with the following details to -

    • Attach 1 or 2 screenshots of your form
    • Provide a brief description of the form
    • You may also attach the XSN file (optional)

    The most popular submissions will be featured on our blog in future posts.

    Check out other Cool Forms here.

  • Microsoft InfoPath 2010

    Powerful Declarative Logic: Phone Number Parsing


    When entering phone numbers in InfoPath, you can validate that it is a phone number easily enough (Data Validation->field1 “matches pattern” Phone number), but what do you do if the input does not match that pattern? Asking users to exactly enter the format “(000) 000-0000” may be a little constraining. Therefore, you may want a rule so that any combination of spaces, dashes, parenthesis and 10 digits will be reformatted nicely.  Below I will describe how to do this in a rule, although you could do the same in business logic. 

    For the rule’s action, you would want to use translate, substring and concat functions.  Logically, as your first action you would translate to remove all unwanted characters (spaces, dashes and parenthesis).  Then, as your second action substring and concat everything. 

    However, you cannot break it into two actions.  When the translate is executed, it changes the text field.  InfoPath immediately reruns the rule on that field (even before the rest of the actions are run from the first time).  For the second run of this rule, the conditions will all pass (as you would have a number like “0123456789” due to the translate that already happened), and the translate action would be run again.  This will happen repeatedly and causes a recursion error. 

    Therefore, you would need to do this all in one action (the rule will be rerun here, but the conditions will not pass since it will match the phone number pattern).

    On your text box, you will need:

    1. One condition with 3 parts:

    - Field “does not match pattern” phone number “and”

    - Without the “()- “ characters, it has length 10:
    “The expression” string-length(translate(., "()- ", "")) = 10 “and”

    - There are no characters other than digits and “()- “ characters
    “The expression” string-length(translate(translate(., "()- 0123456789", "")) = 0

    2. One action that removes (), - and spaces; takes substrings; and concatenates it all together at once:

    Set a field’s value “.” to concat("(",substring(translate(., "()-", ""), 1, 3), ")", substring(translate(., "()-", ""), 4, 3), "-", substring(translate(., "()-", ""), 7, 4)))

    And you're done! Note that this technique will work in InfoPath 2003 and 2007, and it is supported in browser-enabled form templates. Download the form template that has this trick implemented; make sure to save it to your desktop.

    Nicholas Lovell
    Software Design Engineer
    Alexei Levenkov
    Software Design Engineer

  • Microsoft InfoPath 2010

    Getting the XPath without straining your brain

    Yesterday I talked about using System.Xml in the new object model. My code examples included a key part of the InfoPath programming model: looking up fields in the data source using XPath so that you can set and get values.
    Unfortunately, figuring out the absolute XPath to a field can be a pain.
    Fortunately, we've made that easy in InfoPath 2007.
    Here's how
    Right click the field in the Data Source task pane and click Copy XPath
    Now you can paste the XPath into your code and be on your merry way.
    Hope that helps,
  • Microsoft InfoPath 2010

    Advanced server-side authentication for data connections, part 2


    In the first part of this series I introduced the concepts involved in three tier authentication. Now let's drill into the details and work with some code.


    Using Office Single Sign-on  with individual mapping

    An Office Single Sign-on (SSO) application definition, can be set up in one of three ways:  individual, group, or group using restricted credentials.  An InfoPath form published to the server can use either of the first two.  However, in order to use individual credentials, the SSO database must contain a separate username and password for each user who connects to the form.  A typical server application would check whether the connecting user had credentials in the database, and redirect to a credentials management page when appropriate to allow the user to enter their credentials.  While InfoPath Forms Services does not do this generically for forms that use SSO, it’s possible to implement a solution for an individual form or a set of forms the same way an Office Server application would, by using the Pluggable SSO API.
    The general approach is to have users link to an ASP.NET page rather than directly to the form.  The ASP.NET page attempts to get the user’s credentials from the SSO database, and based on the result either redirects to the form or to a credential management page  which allows the user to input their credentials.
    Enough talk.  Let’s get our hands dirty.
    My code assumes that you have defined an SSO application definition called MyApp and that the application is set up to use individual credentials.  I also assume that that have an administrator-approved form template called myform.xsn activated on the site collection, and that the form uses one or more data connections that reference the MyApp application.
    In order to use SSO to make the data connection, you must be using data connection settings in a UDC file on the server.  To add the reference to the MyApp app definition, you add an Authentication element to the UDC file.  The authentication element must be the last subelement of the ConnectionInfo element, and it looks like this:
      <udc:SSO AppId="MyApp" CredentialType="NTLM"></udc:SSO>
    To create the redirector page, the first thing is to add a new ASP.NET page to the root site of your SharePoint server.  Find the home directory of your root site by opening up Internet Services Manager and checking the Home Directory page of the properties page for the web application.  Open this web in Visual Studio and add a new ASP.NET page.  In the properties  for the page, enable session state.  Finally, add a reference to the Microsoft.SharePoint.Portal.SingleSignon namespace.
    Once this is done, the new page should look like this:
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="SingleSignonRedirector.aspx.cs" Inherits="SingleSignonRedirector" EnableSessionState="True" %>
    <%@ Import Namespace="Microsoft.SharePoint.Portal.SingleSignon" %>
    Open the code file for the page, and add a using statement for the SingleSignon namespace
    using Microsoft.SharePoint.Portal.SingleSignon;
    The code itself is relatively straightforward.  Start by using the pluggable API to get a reference to the installed SSO provider.
    ISsoProvider provider = SsoProviderFactory.GetSsoProvider();
    (this call will fail if no provider is installed, or if the Single Signon service is not running)
    Your page will run under the credentials of the user attempting to access the form, so when you call GetCredentials, SSO will return a credential for that user, or throw SingleSignonCredsNotFoundException if the credential does not exist.
       SsoCredentials credentials = provider.GetCredentials("MyApp");
    catch (SingleSignonCredsNotFoundException)
    In the event that the credentials are not found, you can get the URL of the credentials management page for the installed provider.
    Uri CredentialsManagementUrl = provider.GetCredentialManagementURL("MyApp");
    Now, at this point you could simply redirect to the credentials management URL.  However, you would miss part of the magic of the credentials management page.  The credentials management page supplied with the Office single Sign-on provider will redirect back to the referring page once the user has entered valid credentials.  I chose to handle this by presenting a link to the user to allow them to click through to the credentials management page.
    Response.Write("<P>Before you can access this form, you must enter credentials to access backend data.</P>");
    Response.Write("<P><A href=\"" + CredentialsManagementUrl.ToString() + "\">Click here to enter your credentials</A></P>");
    An alternate approach could be to add the HTTP referer header to the response before performing a redirect.  Either way, once the user has entered their credentials, the redirector page will be called again.  This time the GetCredentials call will succeed, and the page will redirect on to the form.
    Here’s the complete code-behind for the page:
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using Microsoft.SharePoint.Portal.SingleSignon;
    public partial class SingleSignonRedirector : System.Web.UI.Page
        protected void Page_Load(object sender, EventArgs e)
            ISsoProvider provider = SsoProviderFactory.GetSsoProvider();
                SsoCredentials credentials = provider.GetCredentials("MyApp");
            catch (SingleSignonCredsNotFoundException)
                Uri CredentialsManagementUrl = provider.GetCredentialManagementURL("MyApp");
                Response.Write("<P>Before you can access this form, you must enter credentials to access backend data.</P>");
                Response.Write("<P><A href=\"" + CredentialsManagementUrl.ToString() + "\">Click here to enter your credentials</A></P>");
            catch (Exception ex)
                Response.Write("<P>Single Sign-on returned an error:  " + ex.Message + "</P>");
            // Good to go - on to the form
    Response.Redirect( "http://myserver/formservertemplates/myform.xsn?openin=browser" );
    Getting jiggy with it
    For a real-world application, there are a few more things I might do to this page to round out the implementation.  Depending on the needs of the specific application, one or more of the following might apply:
    • Loop through multiple Application IDs to allow the user to enter credentials for each
    • Attempt to log on the user using the SSO credentials to verify that the credentials are valid and have not expired
    • Use this code in a page which hosts the form in question using the XmlformView control
    • Create a generic page which takes the URL to the form and the Application ID as query parameters
    With a little bit of ingenuity, you can use this approach to create a seamless end-to-end experience to satisfy any scenario.
    In my next and final post in this series, I'll talk about how to use a web service proxy for authentication. Stay tuned!
    - Nick
    Program Manager
  • Microsoft InfoPath 2010

    Using Relink When Moving a SharePoint Form Library to a New Site


    When InfoPath form files are saved to a SharePoint Form Library, SharePoint creates hard-coded references to the form template in the team site where the forms are being saved.  If the SharePoint Form Library is moved to a new location, the references in the InfoPath form files will continue to point to the old location, even after they've been moved into the new Form Library. In order to fix up the references in the form files to point to the new form template location, you need to perform a Relink operation.

    Important   Before performing the Relink operation, make sure that the Windows SharePoint Services server you are working with has been updated with Windows SharePoint Services Service Pack 1. This service pack release fixes a bug that can cause data corruption when performing a Relink operation.

    Note   You cannot relink a form if the all data in the form has been digitally signed (the form template has been configured for signing by using the Enable digital signatures for the entire form option, or by using the Enable digital signatures for specific data in the form option with the Set of signable data specified using an XPath expression of "/"). This is because when a form has been signed using this configuration the link to the form template is treated as part of the form data, and relinking would invalidate the signature. You can relink a form that has only part of the data signed (the form template has been configured for signing by using the Enable digital signatures for specific data in the form option with the Set of signable data specified as a subset of the data). For more information on using digital signatures, see Digitally Signing Data in InfoPath 2003.

    Here are the steps for performing a Relink operation:

    1. Open the new form library.
    2. Click Modify Columns and Settings to the left of the view.
    3. Under General Settings, click Relink forms to this form library.
    4. In the view presented, select all of the forms that you want to relink.
    5. Click the Relink button.

    The references in all of the forms you selected in step 4 will now point to the current form library location.

    Note   It is also possible to update the references in the form files using the Processing Instruction Update Tool provided with the InfoPath 2003 Software Development Kit (SDK); however in most cases the using the procedure above is simpler.

  • Microsoft InfoPath 2010

    Complex Data Validation

    How do you test more than 5 parameters? How do you group parameters? One answer to both questions, is to have multiple validations in one statement. We'll look into these problems in detail in the case studies below.

    Case Study #1

    Problem: a form designer wants to use this logic:

    IF (State="Ohio" or State="Alabama" or State="Arizona" or State="Georgia" or State="Utah" or State="Idaho" or State="Iowa") THEN (fail...)

    Since the Validation UI only supports 5 statements, you run out of room before entering all of the tests.

    Solution: Put more than one test in a statement. If you aren't sure of the syntax, follow along.

    Enter the first three tests.

    Click [OK] to close the dialog. Notice that the syntax is displayed in this dialog:

    Click [Modify] and use this syntax.

    When you change the first column to "The expression", you will see how to refer to the field you are checking. This differs depending on if you are checking the current field or a different field. In this case, State is the selected field, so we can enter the validation like this:

    If you OK this and then come back to it, InfoPath will automatically break out the last 4 tests into separate statements. This makes it easier to see more of the conditions that are being evaluated.


    Case Study #2

    Problem: a form designer wants to use this logic:

    IF (State="Ohio" or State="Alabama") and TaxRate is blank THEN (fail...)

    Using just the default Validation UI, you can't group tests like this.

    Solution:  Put more than one test in a statement. Again, if you aren't certain of the syntax, enter the tests in the Validation dialog:

    Click [OK] to close. Note the syntax in this dialog:

    Click [Modify], and use the syntax shown in the Validation statements.


    Alternative Approach

    Another way to handle both of these scenarios is to use multiple validations:

    This will work and in some cases would be easier to follow (the 50 states could be listed and you could scroll the Validations).

    Performance may be better in one or the other, but that would depend on the logic you are validating.

    All of these statements are evaluated. There is not an implied "and" or "or" combining them. In this last example, if you were checking the "State = ...  OR TaxRate is blank", both would fail. You could check the number of errors and would get 2, even though only 1 error message would be shown (the first one).

    Jerry Thomas
    Software Design Engineer in Test

  • Microsoft InfoPath 2010

    Hosting InfoPath forms in a custom ASPX page


    Many of you saw a detailed MSDN article on embedding an InfoPath XmlFormView control into a custom ASP.NET page. But - there's more to it. I came across an interesting blog post that talks about embedding a browser-based InfoPath form into a webpart. Here's another post of someone who got it to work, with nice screenshots.

    Alex Weinstein
    Program Manager

  • Microsoft InfoPath 2010

    Displaying Contextual Help in a Custom Task Pane


    With all the wonderful features in the new Office apps, it's easy to get lost!  InfoPath is no exception, and when you make clever use of the new features, you'll want to make sure that users understand how your form is supposed to work.  Wouldn't it be great if you could display contextual help information as the user navigates to fields in your form.  With the "Context Changed" event, you can execute custom code when the user filling the form causes the context node to change.  This will fire when the user focuses a control bound to a different DOM node than the current context node.


    Files you'll need

    The solution we provide here uses some resource files.  Download the attached files to accomplish the scenario.

    1. "CustomTaskPaneHelp.htm" - The HTML file that will be displayed in the custom task pane.  We will manipulate this document at runtime from custom form code to display the contextually appropriate help information.  Just update the styles in this file to change the appearance of the contextual help in the custom taskpane.

    2. "Help Strings.xml" - This file is a basic xml file that contains mappings from local node names to the title and help contents for the node.  This file will be consulted to retrieve the help information to be displayed in the custom task pane.

    3. "CustomTaskPane.cs" - This helper class wraps the HtmlTaskPane object to access elements in the custom task pane.  This will be added to your VSTA project.

    4. "FormCode.cs" - This contains the code to integrate the custom taskpane into the form template.

    Note that this method works in InfoPath 2003 and 2007, but this tutorial will walk you through the steps in for InfoPath 2007 - the UI is slightly different in 2003, but you can still make it work. Since task panes are a rich-client only feature, this method is not supported in browser-enabled form templates.


    Construct your help resource file

    Assuming you've designed your form template already, you'll have a nicely populated data source, replete with variously named nodes.  Now add contextual help data for nodes in the main and secondary data sources:

    1. Open the "Help Strings.xml" file in notepad (or another text editor.)

    2. Add a "Help" element for the data source node:

    • Set the "nodeName" attribute's value to "<node prefix>:<Name in Data Source Pane>"
    • Set the "Title" element's value to the heading that you want to display for this node's contextual help.
    • Set the "Contents" element's value to the body of this node's contextual help.

    3. Repeat step 2 for all nodes for which you want to display contextual help so that you have something like this:

    <?xml version="1.0" encoding="utf-8" ?>
          <Help nodeName="my:TextField">
             <Title>Text Field</Title>
             <Contents>Help for my text field.</Contents>
          <Help nodeName="my:RichTextField">
             <Title>Rich Text Field</Title>
             <Contents>Help for my rich text field.</Contents>


    4. Save the file to a shared location

    NOTE:  If you want to be able to update this help data without having to re-deploy the form template, the shared location should be accessible to all users that will fill out the form.  If, on the other hand, you do not care about that, then you can just save the file locally and add it as a resource file to the form template.  See below…


    Add the data source that will provide your contextual help data

    Now that you're help strings are all set, we need to add a reference to the help strings so that the form template can access them.  We'll do this by adding a data connection to query the xml file from a shared location or resource file.

    1. Tools >> Data Connections >> Add

    2. Select Receive Data, then XML Document

    3. Specify the location of the help strings document (Help Strings.xml)

    • If, as noted above, you want to include the help strings as a resource file in the form template, just click "Next >". Including the file as a resource file guarantees that it will always be accessible to the form template, but the form template will have to be updated if you need to change the help strings.
    • If, on the other hand, you would like to access the help strings file from a shared network location or web server, then select "Access the data from the specified location" and then click "Next >". Accessing the file from a shared location may fail if the shared location is unavailable, and there may be some delay due to network traffic.

    4. Check the "Automatically retrieve data when form is opened" check box and click "Finish" / "Close".


    Enable the custom task pane in your form template

    Custom task panes are not enabled by default.  We'll specify that the custom task pane should be displayed, and its content should be rendered from the "CustomTaskPaneHelp.htm" file.

    1. Tools >> Form Options >> Programming

    2. Check the "Enable custom task pane" check box. Type a name for the custom task pane in the "Task pane name" field.

    3. Add the "CustomTaskPaneHelp.htm" file as a resource file.

    • Click Resource Files >> Add…
    • Browse to the "CustomTaskPaneHelp.htm" file and click Open / OK.

    4. Select "CustomTaskPaneHelp.htm" from the "Task pane location" drop-down list.


    Add the code to manage the context change and display the contextual help information

    1. Tools >> Programming >> Context Changed Event; this will create the VSTA project and insert the "Context Changed" event handler.

    2. Add the "CustomTaskPane.cs" file to your project

    • Right-click the top-level project node.
    • Click "Add" >> "Existing Item…"
    • Browse to the "CustomTaskPane.cs" file and click "Add".
    • IMPORTANT: Edit the Namespace of the "CustomTaskPane.cs" file to be the same as that of your form code file.

    3. Integrate the code in the attached "FormCode.cs" file into your VSTA project.  Make sure you copy the contents of the "FormEvents_ContextChanged" method, as well as the private properties and variables.

    4. Build and save your form template.



    Make the form template full trust

    1. Tools >> Form Options >> Security and Trust
    2. Uncheck the "Automatically determine security level" checkbox. Select the "Full Trust" radio button.
    3. Click "OK"
    4. Save and publish the form template. You will need to publish the form template as an installable form template (.msi) or digitally sign the template; detailed instructions are here.

    Now, when users fill out your form, contextual help will be displayed for each node as the user clicks on a control bound to it.  As long as you've included help information in the help strings file, you'll see the help information in your custom task pane!

    Forrest Dillaway
    Software Design Engineer in Test

  • Microsoft InfoPath 2010

    Understanding the InfoPath SP1 URN Form Conflict Dialog


    Background on URNs

    InfoPath SP1 included changes to the mail deployment model to allow for easier ways to distribute form templates to a group.  One of the tools for making this deployment model work is the URN (Uniform Resource Name).  These URNs are generated automatically by InfoPath and should be unique for each new form template you build. 

    Note:  It’s possible to get a URN that’s identical to an existing one, but not very likely.  This will be explained more later on.

    To see the URN for your form template, you can select ‘File | Properties’ when in the Designer.  The URN is listed there as the ‘Form ID’.  It is called a Form ID because it is used in the deployment model to identify a form template that may be opened from different locations.  The URN is generated by combining three sections separated by semicolons based in part on the form template’s properties.  Here’s a sample URN:


    It consists of three parts:

    • Prefix:  [urn:schemas-microsoft-com:office:infopath]
    • Form Name (the filename by default):  [blogsample]
    • The Namespace or Namespace Timestamp:  [-myXSD-2004-05-19T20-48-18]

    Note:  The Namespace Timestamp is used whenever a user starts out building the schema within InfoPath.  If a form template is built from an XML or XSD file which has its own Namespace, then that Namespace will be used. 

    The Conflict Dialog

    Picture of the URN conflict dialog 

    This dialog will appear any time that you open a form template on your machine that has a URN that matches an existing URN that you’ve previously opened and cached, but references a different Access Path (or publish location, URL).  As you can see in the example above, I have a file called ‘blogsample.xsn’ that I’ve opened from My Documents, but the conflict dialog is saying that I’ve previously opened a file with the same URN from my Desktop.  I got myself into this situation by publishing the same form template to two separate locations with the same filename and then opening each of them.  Because the Form Name and Namespace timestamp are identical for both XSNs, so is the URN. 

    There are two options at this point.  I can either open the newer template (Version by clicking ‘Replace Form on Your Computer’ or the older one (Version by clicking ‘Keep Form on Your Computer’.  It is important to note that this dialog does not affect the original copies of these files in any way.  It will only update your local cache and it will update the file that is shown with this Form Name in your Fill Out a Form dialog.

  • Microsoft InfoPath 2010

    Capacity Planning for SharePoint 2010


    Check out the new Capacity Management Resource Center for SharePoint 2010 that went live when Office 2010 released to manufacturing last Friday, April 16th.  The InfoPath capacity planning document  along with a refresh of the Capacity Planning Tool Kit are now available.

    The Capacity Management Resource Center contains resources to help you with capacity management in your Microsoft SharePoint Server 2010 environment—map your solution design to a farm size and set of hardware that supports your business goals.

  • Microsoft InfoPath 2010

    Browser Forms with Spell Check

    With InfoPath Forms Services, you can take powerful InfoPath forms, and allow users to fill them out by using a browser. This enables your forms to reach more customers than ever before. Many Office users have been enjoying the convenience of spell check in Word and InfoPath. To enable this valuable feature for browser forms, we recommend pointing your customers to an Internet Explorer extension called ieSpell. It will check the spelling in your document, just like you would expect it to; you even get to store your personal word list or (clever!) point ieSpell to a Microsoft Office dictionary.
    After filling out a form, hit the ieSpell button and it pops up a dialog, similar to the Microsoft Office spell check. If you want to know the meaning of the word, the thesaurus is just a click away.
    Installing the program is quick and easy. Download from here (free for non-commercial use). When you're done, you will see a new button in the IE toolbar, as well as a new Tools menu item.
    If you are interested to learn more about ieSpell and other browser extensions, take a look at this article from the Internet Explorer team blog.
    Pradeep Rasam
    Program Manager
Page 5 of 12 (298 items) «34567»