Microsoft InfoPath 2010
The official blog of the Microsoft InfoPath team

December, 2006

  • Microsoft InfoPath 2010

    Another Way of Sorting Repeating Data

    • 12 Comments

    If you have ever attempted to sort repeating node data (i.e. a Repeating Table or Repeating Section) on an InfoPath form, you will find this functionality is not available through the UI. However, using .NET classes you can easily implement a sorting routine that will work in both client and browser scenarios. We discussed one way to make this happen through custom code in a recent article; this post will show a different way to make it happen. We will take a look at how to implement this functionality along with taking advantage of some new features in InfoPath 2007:

    • Dynamic button labels
    • Complex default value on the Button label to change the caption based on the sort order of the selected field (discussed in this article)

    In this sample scenario, let’s assume you are capturing the following data in a Repeating Table:

    • Last Name (name: LastName)
    • First Name (name: FirstName)
    • Age (name: Age)

    In addition, you want to allow your users to select the field they want to sort on (using a button in the column header) and clicking the button will toggle the sort between Ascending and Descending. Here is a sample form showing those options:

    ** NOTE: Notice the “(Asc)” in the Last Name button label? We’ll show you how to do that at the end of this post!

    The data structure for the above sample is as follows:

    So in this scenario, the user would simply click the button above the field they want to use to sort the data and by default, the first click would sort the data in Ascending order and clicking it again would sort the data in Descending order. Let’s now go ahead and take a look at the code on the click event of the buttons that implements this functionality.

    When each button is clicked, the first thing we do is set the value of the SortOrder and SortField nodes. For ease of implementation, we created a “SpecifySortOptions” procedure that is called from the click event of each button:

    SpecifySortOptions("LastName",XmlDataType.Text,e.ControlId);

    When each button is clicked we call this procedure passing it the field we want to use for sorting (in this case, LastName), the data type of this field (XmlDataType.Text) and the ControlID of the button that was clicked. (The ControlID is used in the Expression for the Default Value property of each button to determine how to change the label.)

    Here is the SpecifySortOptions procedure:

    public void SpecifySortOptions(string SortField, XmlDataType dataType, string ControlID)

    {

    //Create Navigator objects for the main DOM and

    //for the SortOrder and SortField fields

    XPathNavigator xn = this.MainDataSource.CreateNavigator();

    XPathNavigator xnSortOrder = xn.SelectSingleNode("/my:myFields/my:SortOrder", this.NamespaceManager);

    XPathNavigator xnSortField = xn.SelectSingleNode("/my:myFields/my:SortField", this.NamespaceManager);

     

    //Check to see if the value of the SortField is equal

    //to the ControlID that we passed to this procedure. If

    //it is the same and the SortOrder field is an SortOrder

    //emptry string or is set to "Desc" then set the field to

    //"Asc". If the SortField value does not equal the

    //ControlID that we passed to this procedure, then that

    //would mean either the SortField is an empty string or

    //it was set to another field - either way, we will

    //then want the SortOrder value to be "Asc"

    if (xnSortField.Value == ControlID)

    {

       if (xnSortOrder.Value == "" || xnSortOrder.Value == "Desc")

          xnSortOrder.SetValue("Asc");

       else

          xnSortOrder.SetValue("Desc");

    }

    else

       xnSortOrder.SetValue("Asc");

     

    //Call the SortTheData() procedure passing in the values

    //specified above

    SortTheData(SortField, xnSortOrder.Value, dataType);

     

    //Set the SortField value to the current ControlID

    xnSortField.SetValue(ControlID);

    }

    After calling the SpecifySortOptions procedure from the click event of each button, this procedure calls the SortTheData procedure, which accepts a string value for the sort field (strSortField), a string value for the sort order (strSortOrder) and an XmlDataType value (dataType) for the type of data being sorted. This is the code that will actually perform the sorting.

    The first thing we need to do is this procedure is create “XPathNavigator” objects for the main DOM:

    //Create a Navigator object for the main DOM

    XPathNavigator xn = this.MainDataSource.CreateNavigator();

    We then will create an XmlSortOrder object so we can specify either an Ascending or Descending sort. In this sample, we will specify an Ascending sort as the default; however, we will check the value of strSortOrder and if this is set to “Desc”, change the XmlSortOrder object accordingly:

    XmlSortOrder sortOrder = XmlSortOrder.Ascending;

    if (strSortOrder == "Desc")

    sortOrder = XmlSortOrder.Descending;

    To actually perform the sort, we will be using the “AddSort” method of an XPathExpression object – as such, we need to create an XPathExpression object for the repeating (group) node that we are going sort:

    XPathExpression xe = xn.Compile("/my:myFields/my:group1/my:group2");

    Now we can use the AddSort method on the Expression object using the field name (strSortField) that we passed into this procedure, the sort order using the sort order object (sortOrder) we created above and the data type using the data type object (dataType) we passed into this procedure:

    xe.AddSort("*[local-name()='" + strSortField + "']", sortOrder, XmlCaseOrder.None, "", dataType);

    We need to specify a NamespaceManager for the Expression object and for this we will use the SetContext method:

    xe.SetContext(this.NamespaceManager);

    The next step is to create an XPathNodeIterator object, passing it our XPathExpression object, so we can iterate all the nodes now that they are sorted - in addition, we will use this object (in the lastNode expression below) to get a count of the total nodes in this repeating group:

    XPathNodeIterator xi = xn.Select(xe);

    In the end, the way this procedure works is to delete the existing “un-sorted” nodes and add back the “sorted” nodes via the XPathNodeIterator object. So the next step is to now delete the existing “un-sorted” data. To do this, we will create XPathNavigator objects to reference the first and last nodes in this repeating group and then use the DeleteRange method to delete those nodes:

    XPathNavigator firstNode = xn.SelectSingleNode("/my:myFields/my:group1/my:group2[1]", this.NamespaceManager);

    XPathNavigator lastNode = xn.SelectSingleNode("/my:myFields/my:group1/my:group2[" + xi.Count + "]", this.NamespaceManager);

    firstNode.DeleteRange(lastNode);

    At this point, we have the sorted data in memory and the un-sorted data has been removed so we are ready to add that sorted data back to the form. For this process, we will use the XPathNodeIterator object we created earlier to iterate over the nodes.

    while (xi.MoveNext())

    {

    //Create string variables to hold the values of each field

    //as we iterate the nodes

    string strLastName = xi.Current.SelectSingleNode("my:LastName", this.NamespaceManager).Value;

    string strFirstName = xi.Current.SelectSingleNode("my:FirstName", this.NamespaceManager).Value;

    string strAge = xi.Current.SelectSingleNode("my:Age", this.NamespaceManager).Value;

     

    //Call the AddNewRow method to append a new row

    // to the repeating group

    AddNewRow(xn.SelectSingleNode("/my:myFields/my:group1", this.NamespaceManager));

     

    //Since we are continually appending new rows, the 

    //"last" row will always be the one where we need

    //to set the values - so here we will create a

    //Navigator object for this newly added row - we

    //will use this for setting the field values below

    XPathNavigator xnNewRow = xn.SelectSingleNode("/my:myFields/my:group1/my:group2[last()]", this.NamespaceManager);

     

    xnNewRow.SelectSingleNode("my:LastName", this.NamespaceManager).SetValue(strLastName);

    xnNewRow.SelectSingleNode("my:FirstName", this.NamespaceManager).SetValue(strFirstName);

     

    //Since the Age field is numeric, it will contain

    //the "nil" attribute. We need to remove this

    //arrtibute prior to programmatically setting the

    //value. To do this, we'll call the DeleteNil

    //procedure passing it the node that contains

    //(or may contain) the nil attribute

    DeleteNil(xnNewRow.SelectSingleNode("my:Age", this.NamespaceManager));

     

    //Now we can set the value of the Age field

    xnNewRow.SelectSingleNode("my:Age", this.NamespaceManager).SetValue(strAge);

    }

    In the while loop above, we used the “AddNewRow” and “DeleteNil” procedures – these are documented below:

    public void AddNewRow(XPathNavigator docXN)

    {

    //Create a Navigator object to reference the node

    //we will be adding. To do this, we can use the

    //templates' "Manifest.xsf" file to get the

    //appropriate node to add. As you can see, this is

    //specific to the control's "name", which you can

    //get from the Advanced tab on the Properties window

    //for the repeating control. Once you have this,

    //use the "Save As Source Files" command from the 

    //File menu in InfoPath and locate the appropriate

    //expression in your Manifest.xsf file

    XPathNavigator xnNode = this.Template.Manifest.SelectSingleNode("//xsf:xDocumentClass/xsf:views/xsf:view/xsf:editing/xsf:xmlToEdit[@name='group2_1']/xsf:editWith/xsf:fragmentToInsert/xsf:chooseFragment/my:group1", this.NamespaceManager);     

     

    //Append the node from the Manifest file to the main DOM

    docXN.SelectSingleNode("/my:myFields/my:group1", this.NamespaceManager).AppendChild(xnNode.InnerXml);

    }

     

    public void DeleteNil(XPathNavigator node)

    {

    //Check to see if the nil attribute exists

    //and if so, delete it

    if (node.MoveToAttribute("nil", http://www.w3.org/2001/XMLSchema-instance))

        node.DeleteSelf();

    }

    And that’s it! You now have the functionality of sorting data in a repeating node. For reference, the complete code for this sample is attached.

    Now – about that button label…how did we do that??!

    With InfoPath 2007, we have a new feature that allows you to specify a dynamic value for the button label. To do this, simply click the “fx” button next to the label property and you can choose to use an expression of a field/group from your form:

    However, for this sample the conditional logic for the button label is quite complex: we need to determine which button was clicked and whether we should show “(Asc)” or “(Desc)” next to the correct label. For this, we used the process demonstrated in this blog post: Conditional Default Values.
    So here is the logic that needed to be implemented when each button is clicked; for example, for the Last Name button:

    • See if the SortField value (which is set to a button’s ControlID in the SpecifySortOptions procedure) is equal to the clicked button’s ControlID and if the SortOrder value is either an empty string or equal to “Asc”
      • If it is, then set the label to: Last Name (Asc)
    • See if the SortField value is equal to the clicked button’s ControlID and the SortOrder value is “Desc”
      • If it is, then set the lable to: Last Name (Desc)
    • If neither of the above are true, then a different button must have been clicked so set the label to: Last Name

    This is the logic that needs to be implemented for each button. Here is a sample expression for the LastName field:

    concat(substring("Last Name (Asc)", 1, ((my:SortOrder = "" or my:SortOrder = "Asc") and my:SortField = "btnLastName") * string-length("Last Name (Asc)")), substring("Last Name (Desc)", 1, (my:SortOrder = "Desc" and my:SortField = "btnLastName") * string-length("Last Name (Desc)")), substring("Last Name", 1, not(my:SortField = "btnLastName") * string-length("Last Name")))

    Each of the above “substring” expressions are tested in order – so if the SortOrder field does not equal an empty string or does not equal “Asc” and the SortField value does not equal “btnLastName” then we test the next condition. If the SortOrder value does not equal “Desc” and the SortField value does not equal “btnLastName” then we test the last condition. And here we only need to check the value of my:SortField – if this does not equal “btnLastName” then we know a different button was clicked and we only want the label to display “Last Name”.

    So there you have it! A way to sort data in your repeating table and a really cool way to let the user know which field they clicked for sorting and in which order the data has been sorted!

    ** NOTE: It seems we may have a bug with our expression box in that it will accept the entire conditional statement noted above but once you close and re-open the box, the string gets truncated. Once you have this working, you may want to keep that expression saved in a text file.

    Scott Heim
    Support Engineer

  • Microsoft InfoPath 2010

    Using SQL Server 2005 Web Services with InfoPath

    • 11 Comments

    Here’s your problem: You want to use a stored procedure in a database to access your data through InfoPath, but you don’t really want to have to write script for every query to change the stored procedure’s query parameters.  We’ve all been there.

    Well, stop your coding, right now, because SQL Server 2005 allows you to create SOAP Web Service “endpoints” that act as a type of exposed stored procedure over HTTP.  Among other cool things, this will allow you to have the parameters you need exposed as query parameters in InfoPath’s data source.  It's easy to set up in SQL Server 2005 using the CREATE ENDPOINT T-SQL statement.  For example, let's say there is a simple Stored Procedure called "getAge" that takes an integer value and returns every person in a table that has that age.  The SQL statement to expose that Stored Procedure as a document literal SOAP Web Service could look like this:

    CREATE ENDPOINT getSpecifiedAgeEndpoint

    STATE = STARTED

    AS HTTP

    (

       SITE = 'myserver',

       PATH = '/getspecifiedagewebservice',

       AUTHENTICATION = ( NTLM ),

       PORTS = ( CLEAR )

    )

    FOR SOAP

    (

       WEBMETHOD 'GetRecordsWithSpecifiedAge'

       (

          NAME = 'AdventureWorks.dbo.getAge',

          SCHEMA = DEFAULT,

          FORMAT = ROWSETS_ONLY

       ),

       WSDL = DEFAULT,

       BATCHES = DISABLED,

       DATABASE = 'AdventureWorks'

    )

    The web service will then be located at http://myserver/getspecifiedagewebservice?wsdl.  Note that this web service will not be exposed through IIS; It’s all happening directly from SQL Server 2005.  One method will be exposed at the URL called “GetRecordsWithSpecifiedAge”.  For more information on getting the endpoint up and running correctly, see this MSDN article.

    Note that a few rules apply to the settings that you put in the CREATE ENDPOINT statement that will make it so your Web Service plays nice with the InfoPath Rich Client.  That is, you are probably better off keeping the FORMAT = ROWSETS_ONLY setting as it appears above.  If not, then your web service will return a good deal more information about the query itself than just the rowset data you want, but it will still work for you.  Additionally, setting BATCHES = DISABLED will disable the sqlbatch webmethod from being automatically available on the web service, a powerful webmethod that you should probably keep disabled if you don’t intend to use it.

    At that point, you should be able to design a form template against the web service like any other.  Using SQL Server 2005 rowsets with browser-enabled form templates is currently not a supported scenario; we’ll keep you posted on possible workarounds.

    Also note that when you are designing a main web service against one of these web services that returns row data, InfoPath will warn you in the Data Connection wizard that the web service may return multiple datasets, and that’s an InfoPath no-no.  Under most circumstances, only one dataset will be returned (a dataset can still have multiple tables) so you can usually safely thank InfoPath for letting you know, and continue on.

    Travis Rhodes
    Software Design Engineer in Test

  • Microsoft InfoPath 2010

    Creating Complex InfoPath Controls in C#

    • 8 Comments

    One of the problems with InfoPath controls is their limited availability. For example, there is no such control like the NumericUpDown control in WinForms. Or you may have a predefined complex UI, which you want use multiple times.

    In these circumstances, what we need is a mechanism for control extensibility. Today, InfoPath provides two different control extensibility mechanism: Template parts and ActiveX controls.

    Basically, template parts are predesigned InfoPath form templates, that can be imported into other templates. This mechanism provides a good way of reusing the same controls again and again, but on the dark side, you are still restricted with the control set of InfoPath.

    Other mechanism, which is extensibility via ActiveX controls lets you go beyond the control set of InfoPath and use whichever ActiveX control you want. In this blog, we will drill down into using a Windows control in InfoPath forms, and more importantly provide a way of binding data to these controls. This blog will give details of using WinForm controls that are registered for COM Interop, inside InfoPath forms. Two mainly used types for binding ActiveX controls to InfoPath data sources are 'Simple Binding', ‘Stream Binding’ and 'Node Binding'. In this article we will cover node binding. You can find more information about types of binding between ActiveX controls and InfoPath data source here:

    http://blogs.msdn.com/ajma/archive/2004/07/07/175568.aspx
    http://blogs.msdn.com/ajma/archive/2004/07/08/177512.aspx
    http://blogs.msdn.com/ajma/archive/2004/07/09/178857.aspx

     

    Simple Binding

    If your external control needs to be bound to a single field in InfoPath main data source, then you can use single binding between your ActiveX control and data source. An excellent simple binding example by InfoPath team can be found here: Creating an InfoPath Custom Control using C# and .NET. I highly recommend you to read this article first both to understand how to register our Windows Forms controls for Com Interop, and how simple binding is done for a primitively typed single field. Rest of the article will assume you read and applied the principles told in this article.

    Node Binding

    When binding to a single field or attribute is not enough, or you need your ActiveX controls to interact with data coming from different fields, node binding is the way to go. A classical example would be an ActiveX control capable of showing an Address type complex structure composed of Street, City, State and ZipCode fields.

    In this case, you need an Xml node in InfoPath form template as a data source that obeys the schema of Address object.

    InfoPath form

    We will design the data source of InfoPath first. Let's just write a schema file that corresponds to the structure of Address class. A simple schema file just including address data would be similar to this:

    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

          <xsd:element name="fields">

                <xsd:complexType>

                      <xsd:sequence>

                            <xsd:element ref="address" minOccurs="0"/>

                      </xsd:sequence>

                </xsd:complexType>

          </xsd:element>

          <xsd:element name="address">

                <xsd:complexType>

                      <xsd:sequence>

                            <xsd:element ref="street" minOccurs="0"/>

                            <xsd:element ref="city" minOccurs="0"/>

                            <xsd:element ref="state" minOccurs="0"/>

                            <xsd:element ref="zipCode" minOccurs="0"/>

                      </xsd:sequence>

                </xsd:complexType>

          </xsd:element>

          <xsd:element name="street" type="xsd:string"/>

          <xsd:element name="city" type="xsd:string"/>

          <xsd:element name="state" type="xsd:string"/>

          <xsd:element name="zipCode" type="xsd:integer"/>

    </xsd:schema>

    Start to design a new InfoPath form template from XmlSchema and use this schema as your source. InfoPath should parse the schema, and should create the data source entries in Data Sources tab in design task pane for you. At this point, let's create 4 text boxes that will display each of these fields. For the educational purposes of demo, do not drag and drop the whole address node onto the surface. Instead, drag and drop individual fields on to the design surface. This is because, after designing the ActiveX such that it is bound to whole DOM node, we will get a binding structure like this:

     

    UserControl

    Since we finished the main part of the form template, let's start to prepare the ActiveX object that we will use in the template. Assuming you read the article Creating an InfoPath Custom Control using C# and .NET, I will just touch the points not told there.

    Design the control
    Simply drag and drop four Labels and four TextBoxes on to the user control for each of the fields in Address type.

    Register for ComInterop

    As told in the article, we will need a Guid attribute for Visual Studio to register the control for Com Interop. What is not told in the article is, we need one more attribute to be able to register the control via Visual Studio: ComVisible. Although, UserControl class is donated with ComVisible(true) attribute in .Net framework, we have to state it explicitly one more time, because ComVisible attribute is created with AttributeUsage(…, Inherited = false), which prevents the value of the attribute to be inherited to children classes. So, our control should look similar to this for a successful registration:

    [Guid("08E623D3-BEAD-4bd3-8401-EFF51FD754CD")]

    [ComVisible(true)]

    public class ComplexUserControl : UserControl

    After building this code, a quick search through registry for Guid, will prove that class is really registered.

    Interfaces
    You should import the IObjectSafety interface exactly as told in the article. Before discussing IPropertyNotifySink, let's first take a look at the control interface, that will define the Value and Enabled property. In simple binding example, we had a primitive property in ICOMControl interface. Since we are no longer binding to a primitive field, we have to define a new interface.

    Let's define an IComplexControl interface similar to IComControl interface in the article. For Value property, we will use IXMLDOMNode type defined in Microsoft.Office.Interop.InfoPath.Xml.dll assembly that you will find in your Office 12 installation directory. This corresponds to a node in InfoPath main DOM data source. At the end, our interface should be similar to this:

    [InterfaceType(ComInterfaceType.InterfaceIsDual)]

    public interface IComplexCOMControl

    {

        [DispId(ComplexUserControl.DISPID_VALUE)]

        IXMLDOMNode Value { get; set; }

     

        [DispId(ComplexUserControl.DISPID_ENABLED)]

        bool Enabled { get; set; }

    }

    As stated in the article, Constants.DISPID_VALUE should be 0, and Constants.DISPID_ENABLED should be 1, in order for InfoPath to correctly communicate with our control.

    Now let's inspect IPropertyNotifySink interface. One of the main differences between Simple Binding and Node Binding is the type of the Value property. Simple binding deals with primitive types, whereas node binding deals directly with DOM. In simple binding a rough sequence of events would be like this:

          Property change in data source --> … -->Set Value property of ActiveX

    And the other way around:

          Property change in ActiveX --> Raise OnChanged -->InfoPath queries for Value property --> … -->Set Value property of ActiveX

    However, since InfoPath sends the whole DOM node as our Value property by reference, we don’t need to set an intermediary Value property. Instead we can directly change DOM values from DOM node reference. So we can safely ignore RaiseOnChanged and InfoPath queries for Value property steps. Since IPropertyNotifySink interface is used mainly for property change notification purposes will not implement it in this demo. If you care about Enabled property changes, you still have to implement this interface and notify InfoPath when Enabled property changes.

     

    Value property implementation

    When InfoPath first calls the setter of Value property after instantiating our control, it will pass in the IXMLDOMNode representing the main DOM of InfoPath. We have to keep this node in our control, in order to be able to change the data values corresponding to changes in UI.

    In the getter of the property, since we are not changing the reference of DOM node, it is safe to return this kept value.
    Whenever some field (or subfields in our group field)changes in DOM, InfoPath will call the setter of our Value property. In the setter, we should query the DOM according to XPaths of our fields, and then update the UI according to the values if necessary. At the end, our Value field should look like this:

    private IXMLDOMNode domNode = null;

    public IXMLDOMNode Value

    {

       get

       {

          return domNode;

       }

       set

       {

            if (domNode != null)

            {

                ChangeUI(streetTbx, domNode.selectSingleNode("/fields/address/street").text);

                ChangeUI(cityTbx, domNode.selectSingleNode("/fields/address/city").text);

                ChangeUI(stateTbx, domNode.selectSingleNode("/fields/address/state").text);

                ChangeUI(zipCodeTbx, domNode.selectSingleNode("/fields/address/zipCode").text);

            }

        }

    }

     

    private void ChangeUI(TextBox tbx, string text)

    {

       if (tbx.Text != text)

       {

        tbx.Text = text;

       }

    } 

    At this point we have completed the one direction of binding, originating from data source and going to UI. Now, let's handle the other way around, from UI to data source. In order to do this, we have to create a simple event handler that will catch change events in UI. Then register for all four text boxes' TextChanged events, such that each of them will be handled by this event handler. Code should look like this:

    public ComplexUserControl()

    {

        this.InitializeComponent();

     

        // Register for UI change events

        this.streetTbx.TextChanged += this.HandleChangesInUI;

        this.cityTbx.TextChanged += this.HandleChangesInUI;

        this.stateTbx.TextChanged += this.HandleChangesInUI;

        this.zipCodeTbx.TextChanged += this.HandleChangesInUI;

    }

     

    private void HandleChangesInUI(object sender, EventArgs e)

    {

        TextBox senderTbx = (TextBox)sender;

        if (senderTbx == this.streetTbx)

            this.ChangeDOM("/fields/address/street", this.streetTbx.Text);

        else if (senderTbx == this.cityTbx)

            this.ChangeDOM("/fields/address/city", this.cityTbx.Text);

        else if (senderTbx == this.stateTbx)

            this.ChangeDOM("/fields/address/state", this.stateTbx.Text);

        else if (senderTbx == this.zipCodeTbx)

            this.ChangeDOM("/fields/address/zipCode", this.zipCodeTbx.Text);

    }

     

    private void ChangeDOM(string xPath, string value)

    {

        if (domNode != null)

        {

            IXMLDOMNode singleNode = domNode.selectSingleNode(xPath);

            if (singleNode.text != value)

            {

                singleNode.text = value;

            }

        }

    }

    Integration

    In order to be able to use the ActiveX Control in the design of a form template, we have to make one final trick: Placing the .ict file that describes the control and its binding, to our controls directory. In order to add custom controls design task pane in InfoPath, generally you first go to the Controls tab, then click Add or Remove Custom Controls at the bottom of this tab, select to add a new ActiveX control, and finally select the ActiveX you want to add from the list of registered controls. If you have ever done this before, there should be a directory named

    C:\Documents and Settings\username\Local Settings\Application Data\Microsoft\InfoPath\Controls

    In this directory there should be a .ict file for each of the ActiveX controls that you have done this operation for. We have to create a new .ict file for our control. If above directory does not exist, create it. Then create a file with name "{yourClassGUIDHere}.ict". This file should be in this format:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>

    <ict:control name="Complex C# Control, v1.0" xmlns:ict="http://schemas.microsoft.com/office/infopath/2003/ict" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xdSdl="http://schemas.microsoft.com/office/infopath/2003/xct" xmlns:ict2="ict2" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003">

          <ict:designTimeProperties>

          <ict:iconBitmap>Qk3uAAAAAAAAAHYAAAAoAAAAEAAAAA8AAAABAAQAAAAAAAAAAADEDgAAxA4AABAAAAAQAAAAAAAA/wAAgP8AgAD/AICA/4AAAP+AAID/gIAA/4CAgP/AwMD/AAD//wD/AP8A/////wAA//8A/////wD//////4iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiP//////////eIiIiIiIiI94RIRIRIiIj3hEhEhEiIiPeESESESIiI94RIRIRIiIj3iIiIiIiIiPd3d3d3d3d3eIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiA==</ict:iconBitmap>

          </ict:designTimeProperties>

          <ict:deployment>

                <ict:controlPackage classid="{yourClassGUIDHere}"/>

          </ict:deployment>

          <ict:shapes>

                <!--default shape-->

          </ict:shapes>

          <ict:schemaEditing>

                <ict:any/>

          </ict:schemaEditing>

         <ict:view>

               <ict:controlObject bindingProperty="Value" bindingType="xmlNode" enabledProperty="Enabled" enabledValue="true"/>

         </ict:view>

    </ict:control>

    Note the bolded text. You should place the correct GUID for your control, and note that we are actually specifying 'xmlNode' as bindingType. Rest of the file is almost standard except the iconBitmap part, which specifies the icon that will be displayed next to your control in design task pane.

    Since we have a ready control now, we can safely use it in our form template. Open the InfoPath form you have designed in design mode again. Go to Controls tab in design task pane. At the bottom, under Custom section, you should see the 'Complex C# Control, v1.0' with icon I have copied from Microsoft Forms Progress Bar control. Drag and drop this control to design surface. According to your security settings, it may ask you if you want to run ActiveX control. If so, press 'Yes' and continue. Since we have already defined a data source in the form, InfoPath will ask which field or group do we want our control to bind to. Choose address here. Now you should see the control on the surface, and its binding field is specified as /fields/address. Press Ctrl+Shift+B to preview the form. You should see the corresponding InfoPath TextBoxes and our UserControl's TextBoxes are synchronized, because they are bound to same data source fields.

    Now we have a functional complex WinForm UserControl, that we can display in an InfoPath form template, and even better, this UserControl is bound to InfoPath main DOM.

    Improving ActiveX Discrepancies

    If you played with the form a few minutes, you must have noticed that there is something weird going on. If you are changing the text value of InfoPath TextBoxes, you see a flicker in the UserControl before data is reflected to it. Similarly, if you change the value of a TextBox in your UserControl, you also see a flicker in the UserControl, and focus is continuously given to first TextBox added to UserControl.

    Main reason for this issue is, whenever a data field that is bound to your ActiveX is changed, InfoPath destroys your control and create a new one. This is mainly because InfoPath has to apply your view xsl to your data source to create the view you see on the display. Every time data source changes, InfoPath re-applies the xsl file and creates the view from scratch for controls that are bound to changed data fields. If you are debugging the form, you can easily see this by putting a breakpoint in the constructor of UserControl. Clearly, this leads us to write stateless controls, since each time the controls are destroyed, state is destroyed too. Or, as a second approach, we can take advantage of a helper interface, InfoPathControl interface, to save the state to some persistent storage just before destruction, and retrieve it after re-creation. Let's inspect how this approach works.

    InfoPathControl interface is defined in Microsoft.Office.Interop.InfoPath primary interop assembly. Add a reference to this assembly to your project and implement the interface on your UserControl. I advise you to read the explanation of all four methods of the interface, and how they are used in MSDN. Here, we will only concentrate on two of them, which are Init and SaveState methods.

    SaveState is called by InfoPath, just before the destruction of the older control, which makes it a perfect point to save the state to some storage. Init is called after creating the new control, which lets us to use it to retrieve the saved state and apply it to our control.

    Init method takes a parameter of type InfoPathControlSite. This interface gives us the main DOM node and XDocument of InfoPath form template, which basically enables us to access pretty much all of the object model of InfoPath. So, we don't have to wait for the setter of Value property anymore. We can cache the domNode here in Init method. We will also cache the XDocument for storing the state in, since we will need a persistent storage for our state while we are destroyed. In general, Init method body should be similar to this:

    private XDocumentClass xDocument = null;

    public void Init(InfoPathControlSite pControlSite)

    {

      this.domNode = pControlSite.Node;

      this.xDocument = (XDocumentClass)pControlSite.XDocument;

      this.RestoreState();

    }

    We will inspect RestoreState method after SaveState method.

    For simplicity, we will assume that we only want to keep which control has the focus in UserControl and give focus to that control after re-creation. Saving the name of the active control should be enough for state restoration. In reality in a control like this we should also keep the location of the cursor inside the TextBox as well as which TextBox is active.

    We said that we will use XDocument to save the state. This is because XDocument lives with the form template, so it is persistent during the lifetime of our control, and XDocument has a property bag that we can use for this purpose. We can update this property bag by calling SetNamedNodeProperty and querying it by calling GetNamedNodeProperty method on XDocument object. At the end, SaveState and RestoreState methods should be similar to this:

    public void SaveState()

    {

        if (this.xDocument != null)

        {

            this.xDocument.SetNamedNodeProperty(this.domNode, "ActiveControl", this.ActiveControl.Name);

        }

    }

     

    private void RestoreState()

    {

        if (this.xDocument != null)

        {

            Control activeControl = this.Controls[this.xDocument.GetNamedNodeProperty(domNode, "ActiveControl", this.Controls[0].Name)];

            if (activeControl != null)

            {

                activeControl.Focus();

            }

        }

    } 

    In the call to GetNamedNodeProperty in RestoreState method, third parameter is the default value, which will be returned back if specified property name can't be found in property bag. This will be the case when control is first created.

    Now, each time data source has changed, we save which control was active in UserControl before it is destroyed. Then InfoPath instantiates a new instance, we restore the state if there is any. Then InfoPath sets the Value property with updated fields.

    Saving the whole state in a WinForm control is a non-trivial task and maybe somewhat cumbersome. I'm optimistic about this re-creation of ActiveX controls will be optimized by InfoPath team in the next release.

    At the end, our code will look like this attached file.

    Deniz Demircioglu
    Software Design Engineer

  • Microsoft InfoPath 2010

    XPath Powers: Calculating Totals

    • 6 Comments

    InfoPath makes it trivial to track totals for repeating structures, such as customer orders. Just create a repeating table of line items, and sum up the totals for individual line items. However, sometimes, totals for line items may not be available; it is still possible to perform dynamic calculations across the repeating structure, even when interim results (line item totals) are not stored in the data source. This article will explain a way to make this work without code.


    Let's explore the simple case first.

     Form at Runtime Data Source
      

    Line item total is set by using a default value (price * quantity) on the lineItemTotal node. Creating an order total is just a matter of adding an expression box that uses the built-in SUM function:

    sum(my:group1/my:lineItems/my:lineItemTotal)

    And voila, we're done, totals will be calculated correctly.


    The reason why we are here: the complex case.

    What if the interim results (line item totals in our scenario) cannot be persisted in the data source? This situation might arise if you're operating on a fixed schema, or if you're an "XML purist" (I know I am :-)), arguing that there is unnecessary redundancy in your XML if you store calculated values.

    The goal is still the same - but the data source is different.

     Form at Runtime Data Source
         

    The line item total would be an expression box instead of the text box; it would be calculated simply as (price * quantity).

    But how do we calculate the order total? Your first instinct may suggest to use sum (price * quantity), but you'll soon discover that the SUM XPath function only takes a nodeset...

    Let's recall the clever technique of iterating through repeating items by using just XPath: it was described in detail in this article. Let's use the following value for the order total expression box:

    sum(xdMath:Eval(my:lineItems, "my:price * my:quantity"))

    Why does this work? Let's go inside-out:

    1) The eval function evaluates the price * quantity expression in the context of line items, and returns a nodeset with the results.
    2) The sum function takes in a nodeset as a parameter, and sums up its contents, giving us the desired total.

    I'm attaching a sample form template that has this technique implemented; save the XSN locally before opening it up.

    This method works in InfoPath 2003, 2007, and is supported in browser-enabled form templates.

    Alex Weinstein
    Program Manager

  • Microsoft InfoPath 2010

    Information Rights Management: Protecting Forms

    • 5 Comments

    In a recent post, we discussed ways to protect sensitive data in your InfoPath forms. This article will drill down on one of these mechanisms - Information Rights Management, a.k.a. IRM. InfoPath 2007 features the ability to set permissions on both the form template that you’re designing and the forms that are based on it. You can choose to restrict who can read or write your form/form template, as well as whether read permissions include the ability to copy, print, etc. This feature is available in the both the designer and editor by clicking the Permission button on the Standard toolbar:

    Form instance permissions
    When filling out a form, users can set permissions for that particular form. Clicking on the Permissions button in the Standard toolbar will pop up the following dialog:

    The user that created that instance of the form will have full control permissions and can grant read or write permissions to other users. Users that do not have any permissions to that form will not be able to even open it.

    This feature can make InfoPath a great tool for scenarios where some degree of privacy is required, for example, feedback to a manager to peer.

    Form template permissions
    In addition to setting permissions for each individual form that is filled out, InfoPath also provides Permission functionality for the form template. When designing a form template, clicking on the Permissions button will pop up the Permission dialog:

    The ‘Restrict permission to this form template’ checkbox at the top will initially be unchecked – check it to turn on Permissions. You will then be able to select users or groups that will have different levels of permissions for this form template.

    Keep in mind that this area only allows you to control access to the form template itself, not to the forms that are based upon it. To restrict permissions to the forms based on this form template, you would check the second checkbox, ‘Restrict permission to forms based on this form template’:

    As mentioned for users setting these permissions in the editor, the user that created the instance of the form will always have Full Control permissions for that form instance, but you can get more specific by clicking ‘Change permissions…’. Here you’ll be able to select who will have Read and Change permissions and which additional features will be available to users.

    If you are designing a form for your specific team, you can take advantage of this feature to only give Write rights to members of the team. Everyone else can be given Read rights.

    Managing your credentials
    When working with InfoPath forms or form templates that have restricted permissions, you can select an account to access the content with. For example, if your personal e-mail address does not have permissions to view an expense reporting form, you can set InfoPath to use your work address instead.

    You can access this feature by clicking on Manage Credentials in the File menu:

    Thanks all. I hope you find this useful.

    One thing to keep in mind is that IRM is not a security feature – there are ways to get around it. It’s there to make it more difficult for users to accidentally or maliciously tamper with sensitive data.

    Bojana
    Program Manager

  • Microsoft InfoPath 2010

    Sorting Repeating and Tabular Data

    • 4 Comments

    InfoPath is a great way to easily gather and present XML data to the masses. But what about efficiently presenting a massive amount data? Tons of data (e.g., a list of hundreds product names) usually find their ways into lists that we’ll find on SharePoint, in a database, or just sitting in an XML file. Controls like Repeating Sections and Tables are useful mechanisms for displaying lists of data. But their usefulness is limited to how efficiently the user of your form can find and work with such large lists of data. An easy solution to effectively present large lists of data to your users is through sorting that data.

    No Sorting Options in InfoPath
    Try scouring the Repeating Table or Repeating Section controls’ properties dialogs for anything related to “sort”. Sorry to say, you’ll come up empty handed. That’s why we thought you’d find this blog entry handy. Our goal is to show you how to sort any repeating data and to do so in many different ways: sort by string, sort by number, sort ascending or descending, and even sort by different fields (or columns) in the repeating data. What we’ll show you, in both the code and the design of the view, is designed to be browser-compatible. This means you can publish this form template to a SharePoint server running Forms Services 2007 and fill out the form in a Web browser. Let’s get started.

     

    Figure 1: Filling out the SortingPeople sample form template

    The sample form template is attached; download the ZIP to your desktop, expand it, then right-click the XSN and select "Open in Design Mode". This sample requires InfoPath 2007, and will work in the browser when published to InfoPath Forms Services.

    The View for the SortingPeople Form Template
    To show you how to sort data in your form, we’ll use a concocted example to sort a list of people. As you can see in Figure 1, there are various options to sort the data. Clicking the “Sort” Button triggers the sort otherwise the data is not sorted automatically (more on this later). You can also sort by clicking any of the “Sort by this column” Buttons in the header and footer of any column. There are three columns in the Repeating Table representing our repeating data: Last Name, First Name, and Age. (The string data type is used for the first two columns and an integer data type for the last column.) In Figure 1, we’ve filled out the form with names of famous architects. (Their ages are fictitious.) While we’re not working with enough data to really justify a sort, this example is simply for demonstrative purposes. As you might expect, we can sort the Repeating Table data by any column and by any direction (ascending or descending). At any time we can change the existing data or even add or remove rows. But we would need to invoke the sorting functionality to put things into place again.

    The Data Source for the SortingPeople Form Template
    To begin designing this form template we started with controls and the data source. We inserted a Repeating Table control and renamed its data source groups and fields to map with our people example. The main data source is shown in Figure 2. To accommodate the various sorting options that we expose via controls at the top of the view (e.g., the Sort By Drop-Down List Box), we’ve added attribute fields under the SortingPeople document element. These attribute fields remember, for example, whether we’re sorting by the Last Name or the Age column.

    Figure 2: Data source for the SortingPeople sample form template

    Considerations When Sorting Data
    Now we’ll look at how we implemented sorting functionality behind this form template. This is accomplished through C# code behind the form. While we could have opted for an easier XSL-based implementation to sorting data, it would not have been compatible with Forms Services. So we’re left to actually sorting the data. There are advantages as well as disadvantages to our approach. Sorting the data is preferred when you want to persist the sorting in the saved or submitted form. While the initial cost is much higher to sort the data in the data source instead of the view, there is much less processing that occurs on subsequent visits to that data because it will already be sorted. If your form template has multiple views, switching to and from a view that performs an XSL sort is very expensive. On the contrary, a sorted data source adds no additional processing requirements on view switches. A final reason why you may want to sort the data in the data source instead of the view: submitting sorted data to a database or Web service may be optimal (or even a requirement) for that backend system.

    Sorting Data with C# Form Code
    To best understand how we designed the code behind form template to support sorting, we’ll be talking about its C# form code and how it interacts with the main data source. Let’s start by looking at some of the supporting form code that will make it easier for us to implement the sort feature. The properties are used within the sort itself to read the options at the top of the view about how to sort the data. The methods are very useful helpers that we use throughout our sample code.

    /// <summary>

    /// Returns the "Sort By" Drop-Down value.

    /// The value returned by this property MUST match with an item within the Repeating Table.

    /// </summary>

    private string SortBySelection

    {

        get { return GetValue("@my:SortBy").Replace(" ", string.Empty); }

    }

     

    /// <summary>

    /// Does the user want the SortBy by number (true) or string (false)?

    /// </summary>

    private bool SortAsNumber

    {

        get

        {

            return 0 == GetValue("@my:SortAs").CompareTo("number");

        }

    }

     

    /// <summary>

    /// Does the user want an ascending (asc) or descending (des) sort?

    /// </summary>

    private bool SortAscending

    {

        get

        {

            return 0 == GetValue("@my:Order").CompareTo("asc");

        }

    }

     

    /// <summary>

    /// Helper to wrap an int within brackets.

    /// </summary>

    /// <param name="intToWrap"></param>

    /// <returns></returns>

    private string WrapAsIndexer(int intToWrap)

    { return WrapAsIndexer(intToWrap.ToString()); }

     

    /// <summary>

    /// Helper to wrap a string within brackets.

    /// </summary>

    /// <param name="strToWrap"></param>

    /// <returns></returns>

    private string WrapAsIndexer(string strToWrap)

    { return "[" + strToWrap + "]"; }

     

    /// <summary>

    /// Helper to get an XPathNavigator's value.

    /// </summary>

    /// <param name="xpath"></param>

    /// <returns></returns>

    private string GetValue(string xpath)

    {

        return Root.SelectSingleNode(xpath, NamespaceManager).Value;

    }

     

    /// <summary>

    /// Helper to set an XPathNavigator's value.

    /// </summary>

    /// <param name="xpath"></param>

    /// <param name="value"></param>

    private void SetValue(string xpath, string value)

    {

        Root.SelectSingleNode(xpath, NamespaceManager).SetValue(value);

    }

     

    /// <summary>

    /// Helper to get the document element of the main data source.

    /// </summary>

    private XPathNavigator Root

    {

        get { return CreateNavigator().SelectSingleNode("/my:SortingPeople", NamespaceManager); }

    }

    Next, let’s take a look at the code behind all of the Buttons in our form template. We created these event handlers through each Button’s properties dialog. Their implementations are quite trivial. You can see that adding additional sorting columns to the Repeating Table is a simple task. If you wanted to add a column that doesn’t need to be sorted, there’s nothing to do beyond adding the column in the Table!

    public void SortButton_Clicked(object sender, ClickedEventArgs e)

    {     

        SortList();

    }

     

    public void LastNameSort_Clicked(object sender, ClickedEventArgs e)

    {

        SetValue("@my:SortBy", "Last Name");

        SortList();

    }

     

    public void FirstNameSort_Clicked(object sender, ClickedEventArgs e)

    {

        SetValue("@my:SortBy", "First Name");

        SortList();

    }

     

    public void AgeSort_Clicked(object sender, ClickedEventArgs e)

    {

        SetValue("@my:SortBy", "Age");

        SortList();

    }

    Now the million dollar question: what’s behind the SortList method? Let’s look and then we’ll explain how it works.

    /// <summary>

    /// Bubble sorts the list of people.

    /// </summary>

    private void SortList()

    {

        string sortBy = SortBySelection;

        string itemsToSort = "my:People/my:Person";

        System.Globalization.CultureInfo currentThreadCulture =

            System.Threading.Thread.CurrentThread.CurrentCulture;

     

        int numPeople = Root.Select(itemsToSort, NamespaceManager).Count;

     

        // basic bubble sort implementation

        for (int i = 1; i < numPeople; i++) // xpath is 1-based

        {

            for (int j = i + 1; j <= numPeople; j++) // keep j ahead of i; we can index [numPeople]

            {

                // swap (i,j) if necessary

                string iValue = GetValue(itemsToSort + WrapAsIndexer(i) + "/my:" + sortBy);

                string jValue = GetValue(itemsToSort + WrapAsIndexer(j) + "/my:" + sortBy);

     

                // Do we sort by number or string?

                if (SortAsNumber)

                {

                    int iNum, jNum;

                    if (!Int32.TryParse(iValue, out iNum) || !Int32.TryParse(jValue, out jNum))

                    {

                        // Let InfoPath take care of the invalid datatype with its own validation, we'll keep sorting the rest

                        continue;

                    }

     

                    if ((SortAscending && iNum > jNum) || (!SortAscending && iNum < jNum))

                    {

                        Swap(itemsToSort + WrapAsIndexer(i), itemsToSort + WrapAsIndexer(j));

                    }

                }

                else // SortAsString

                {

                    if ((SortAscending && String.Compare(

                        iValue, jValue, true /*ignoreCase*/, currentThreadCulture) > 0)

                        || (!SortAscending && String.Compare(

                        iValue, jValue, true /*ignoreCase*/, currentThreadCulture) < 0))

                    {

                        Swap(itemsToSort + WrapAsIndexer(i), itemsToSort + WrapAsIndexer(j));

                    }

                }

            } // end inner-for

        } // end outer-for

    }

    Analyzing the C# Form Code
    Let’s break down what we’re doing in this SortList method. First we get the column to use for sorting, the XPath to the repeating group that we want to sort, and the culture of the thread so we respect the current locale when sorting. Next we get the number of people that we’ll be sorting. We need this number because we’ll use it for our bubble sort implementation.

    The two nested for-loops implement the bubble sort algorithm. We chose bubble sort because of its simplicity and for demonstrative purposes. (We’d recommend you use the most efficient sorting algorithm based on your requirements.) The variables i and j iterate through the people. We use the iValue and jValue variables to select the data pointed at by i and j to determine if a swap is necessary as part of the sort loop.

    Next, we have an if-else statement that checks if the sort is by string or by number. A sort by number will attempt to parse out 32-bit integer values from the iValue and jValue fields. If the parse fails for any reason, we skip this specific comparison and continue trying to sort the rest of the data. Once we have integers, we do a simple comparison and swap if needed. If we’re sorting by string instead of numerical value, we use the static .NET library String.Compare method to make a culture sensitive comparison. A swap is performed if it’s necessary. (Note that we could have combined some code in the SortList method to make it more compact. We left the structure of the code unoptimized for maximum readability.)

    The last bit of code we have not yet revealed is the Swap method. This method, as its name suggests, simply swaps the positions of two XPathNavigator objects as identified by their XPaths. (An XPathNavigator is a pointer into an XML tree of data. You can read more about the XPathNavigtor class on MSDN.)

    /// <summary>

    /// Swaps two XPathNavigators at xpath1 and xpath2.

    /// </summary>

    /// <param name="xpath1">First XPath.</param>

    /// <param name="xpath2">Second XPath.</param>

    private void Swap(string xpath1, string xpath2)

    {

            XPathNavigator item1 = Root.SelectSingleNode(xpath1, NamespaceManager);

            XPathNavigator item2 = Root.SelectSingleNode(xpath2, NamespaceManager);

     

            // Make a copy of item1

            XPathNavigator item1Clone = item1.Clone();

            // Move item2 to item1

            item1.ReplaceSelf(item2);

            // Make the original item2 be item1 that we cloned earlier

            item2.ReplaceSelf(item1Clone);

    }

    Sorting Automatically
    One of the things you might ask is why we decided against sorting the data automatically. The most important reason is user experience and then followed by form responsiveness. Think about what would happen if the Repeating Table rows sorted themselves whenever data changed that required a sort. Say you were entering data into a new row. You start by typing a person’s last name and then either hit tab or click into the First Name field. If the form is sorting by the Last Name field, the row may have jumped to another location relative to the other rows in the Repeating Table! You would expect that you could fill in all of the data for a new row before it sorts itself. There are also other weird cases that automatic sorting would spoil. For example, you would not see a new row added within the middle of the Repeating Table. Why? As soon as you added it, it immediately jumped to the top (or bottom) of the Repeating Table as sorted data. Let’s consider for a moment why we didn’t automatically sort because of form responsiveness; for now let’s assume that we somehow worked out all of the kinks with the user model. Obviously the form will be less responsive in InfoPath, especially on a slow computer, with so much sorting. But the real problem is realized when filling out such an intense form template in a Web browser. Every time code runs while a form is filled out in a browser, the form data is posted back to the server for processing. Postbacks themselves can take several seconds or even minutes depending on many variables including the network connection speed as well as the client and server machines’ performance capabilities. As you can see, an automatically sorting form isn’t necessarily a better form.

    Hagen Green
    Software Design Engineer in Test

  • Microsoft InfoPath 2010

    Firing rules when removing InfoPath controls

    • 1 Comments

    The built-in support InfoPath offers for rules can be used to generate a relatively large and powerful set of conditions that trigger a rule action. There are some scenarios though were the default options available through the condition builder may not be sufficient. One of them is firing a rule when certain control has been deleted.

    The above limitation can be worked around by writing form code. However in some cases it may be possible to achieve this scenario only by using the declarative logic InfoPath provides through rules. The pre-condition for this is to have a document schema that allows for some extra helper nodes.

    The workaround uses two extra fields for each control on which we want to have a rule triggered on delete. The fields are simply counters of the old/new number of controls. One of them is bound through an XPath to the current number of the controls (i.e. repeating table rows) we are interesting in. For example, in the attached sample I want to know when a row in a repeating table has been deleted. The XPath expression is simply a count like:

    count(/my:myFields/my:group3/my:group4)

    The second field will keep track of the existing (“old”) number of the controls. We need to set a default value on it corresponding to the number of controls existing when the form is opened. There are two rules applied to the first field to keep the values in sync:

    The first one is used to perform the main action and it fires when the “new” counter is less than the old. The second rule updates the value of the old counter.

    Using the above procedure looks a little bit hacky but it works well for InfoPath 2003/2007. Note that the approach will work for browser-enabled forms also. The sample form template is attached; download XSN to your desktop, then right-click it and select "Open in Design Mode".

    Silviu Ifrim
    Software Design Engineer

  • Microsoft InfoPath 2010

    Two articles from the community

    • 0 Comments

    Making my Technorati rounds searching for InfoPath community content, I came across two very cool articles:

    1) InfoPath 2007, Forms Server, MOSS 2007 by Sahil Malik: start from a Word form, publish it to be filled out in the browser, play with the content type and configure the document library. Fun read, and lots of screenshots.

    2) InfoPath Forms for Mobile Web Browsers by David Gerhardt: good starting point if you're exploring your way around the mobile forms arena. (UPDATE: Fixed link)

    Alex Weinstein
    Program Manager

  • Microsoft InfoPath 2010

    InfoPath Forms Services and AJAX

    • 0 Comments

    There's been lots of well-deserved hype lately around Web 2.0 and the technology that's fueling it, AJAX. We're receiving lots of questions such as "do InfoPath browser forms support AJAX?". The answer is more complex than just yes :-); this article aims to clarify any ambiguities here. But first, some definitions.

    What is AJAX?

    Definition from Wikipedia: Ajax, shorthand for Asynchronous JavaScript and XML, is a web development technique for creating interactive web applications. The intent is to make web pages feel more responsive by exchanging small amounts of data with the server behind the scenes, so that the entire web page does not have to be reloaded each time the user makes a change. This is meant to increase the web page's interactivity, speed, and usability. 

    What is a postback?

    After a page has been loaded, a postback is defined as a subsequent browser request to the web server. Ajax is about asynchronous postbacks - those that allow the user to continue working with the page while the refresh happens.

    Does InfoPath Forms Services use AJAX?

    For a broad set of user actions while editing the form, there will be no postback to the server at all - way faster than an asynchronous postback – and the HTML will be incrementally updated, with no flash. This is because InfoPath Forms Services is capable of translating the declarative logic you've placed in the form into dynamic JavaScript snippets.

    Such client-side operations work for actions such as inserting and deleting sections or table rows, conditional visibility or formatting, validation errors, calculations, firing rules to modify data, etc.

    Cases where postback will occur have one thing in common: there's something that absolutely cannot be evaluated on the client, and thus has to be sent to the server for evaluation. For example:

    - there is managed code in the form which needs to run (e.g. AfterChange event)

    - there are complex XPath expressions used for a calculation, condition for a rule etc   

    When postback does need to happen it generally uses an XMLHTTP postback out-of-band; it does not involve a full page postback. The HTML can therefore be incrementally updated.

    How can I tell when the postbacks will happen?

    We'll only touch on this subject very briefly - expect more content on this soon. InfoPath Design Checker, in conjunction with InfoPath Forms Services, will provide you insights on the user actions that will cause a postback. It will also provide suggestions as to how to avoid these postbacks. Here's how to see these suggestions:

    1. Make sure that your form template is browser-enabled by going to Tools | Form Options | Compatibility, and checking "Design a form template that can be opened in a browser or in InfoPath".

    2. In the same dialog, provide a URL to a server running InfoPath Forms Services:

    3. At the bottom of the Design Checker task pane, click Options, then check the "Show Browser Optimization Messages" checkbox:

    4. Observe the postback optimization messages in the design checker task pane:

    We'll follow-up with a separate article on ways to interpret these messages, and tips and tricks around improving form postback experience.

    AJAX and forms hosted in a custom ASPX page

    You can create custom ASPX pages hosting the InfoPath form control (XmlFormView; more on it here). Everything within the form still uses the above AJAX architecture. But if you need code-behind the page to communicate between the InfoPath form and other ASP.NET controls in the page, then those events will trigger a full-page postback.

    Office Forms Services and ASP.NET AJAX framework

    In MOSS 2007, we don't offer integration with the ASP.NET AJAX framework (also known as Atlas) to allow for partial postbacks involving communication with other ASP.NET controls in the page. Another thing to note is that InfoPath XmlFormView control will not work inside an ASP.NET AJAX UpdatePanel. At this point, there is no workaround to make it work. Integration with the ASP.NET AJAX is a frequent customer request; we're carefully considering it for future releases.

    Alex Weinstein
    Program Manager
    Alexei Levenkov
    Software Design Engineer

  • Microsoft InfoPath 2010

    New Office Online and MSDN Resources

    • 0 Comments

    Take a look new InfoPath-related resources that were recently published to MSDN and Office Online:

    1) The official "What's new in InfoPath 2007" review - excellent way to get to know new stuff that's coming with Office 2007.

    2) A series of video walkthroughs on InfoPath 2007:

    3) InfoPath Glossary - InfoPath comes with quite a few special terms; clarify any ambuguities with this tool.

    Alex Weinstein
    Program Manager

Page 1 of 1 (10 items)