Populate InfoPath repeating table with data from web service


 

It is easy to create InfoPath form template based on a web service. And the beauty of it is that it creates a main data source for you automatically.

However, at times you may want to design your InfoPath form template prior to web service creation due to various reason. One of them might be to quickly publish your template to allow your users to fill in the data, while later in the life of the project is when you start putting some smarts onto the template without having to get users to fill in the same form again. This is exactly my situation with one of my projects. I also want to populate a repeating table using the data returned from calling a web service.

Thus, I embarked on creating POC. The first thing I did is creating a blank form template using 1 textbox and 1 repeating table.

image

 

Because I was aware that I will put a programming logic on this template at later stage, I changed the security for this template to full trust and created a certificate for it.

I published the template to a network location and filled up the form.

Afterward, I grabbed the xsd file of the template. To grab the xsd, I copied and renamed the template to cab file and copy out the xsd. Then, I used Visual Studio command prompt to run xsd.exe to generate a .cs file for my web service (xsd.ex myschema.xsd /c /l:CS /namespace:SkillWebService /o:.)

Then, I created a web service project using Visual Studio. I added the myschema.cs to my project. In the project, I created a web method to retrieve data. I built the data manually through code, but you can extend this to return data from your various repository.

image 

 

After having the web service, I can start putting the code-behind on the InfoPath template. I would override the OnChanged event on the name text box to call the web service method.

I started by adding a secondary data source to connect to the web service and uncheck the option to call the web service when the form is opened. By creating the secondary data source, I can then see the structure of the data I will retrieve.

Before I started putting the code logic, I changed my form options to ensure the programming language being used is C# (It’s faster for me to code in C#). Then, I just added a Changed event on my Name textbox.

Note that, if your web service query may take longer than the default timeout, you should modify the connection timeout on the Loading event.

 

public void Name_Changed(object sender, XmlEventArgs e)
        {
            if (!e.OldValue.Equals(e.NewValue))
            {
                // remove the existing values in the repeating table
                XPathNodeIterator skillIterator = this.MainDataSource.CreateNavigator().Select("/my:myFields/my:Skills/my:Skill", NamespaceManager);

                while (skillIterator.MoveNext())
                {
                    skillIterator.Current.DeleteSelf();
                    skillIterator = this.MainDataSource.CreateNavigator().Select("/my:myFields/my:Skills/my:Skill", NamespaceManager);
                }
                // Get the data connection
                WebServiceConnection retrieveSkills = (WebServiceConnection)this.DataConnections["RetrieveSkills"];

                // Create the XML documents to pass to the web service
                XmlDocument inputDoc = new XmlDocument();
                XmlDocument outputDoc = new XmlDocument();
                XmlDocument errorDoc = new XmlDocument();

                // Create the xml for input parameter
                inputDoc.LoadXml(string.Format("<RetrieveSkills xmlns=\"http://tempuri.org/\"><name>{0}</name></RetrieveSkills>", e.NewValue));

                // Create the XPathNavigator objects for the documents
                XPathNavigator inputNav = inputDoc.CreateNavigator();
                XPathNavigator outputNav = outputDoc.CreateNavigator();
                XPathNavigator errorNav = errorDoc.CreateNavigator();

                // Call web service
                retrieveSkills.Execute(inputNav, outputNav, errorNav);

                // Get the iterator for the output data on skill collection
                XPathNodeIterator outIterator = outputNav.CreateNavigator().Select("/tns:RetrieveSkillsResponse/my:RetrieveSkillsResult/my:Skills/my:Skill", NamespaceManager);

                // Get the current namespace prefix
                string ns = NamespaceManager.LookupNamespace("my");

                // We append the child element for the repeater
                using (XmlWriter writer = this.MainDataSource.CreateNavigator().SelectSingleNode("/my:myFields/my:Skills", NamespaceManager).AppendChild())
                {
                    while (outIterator.MoveNext())
                    {
                        writer.WriteStartElement("Skill", ns);
                        XPathNavigator outCurrent = outIterator.Current;
                        XPathNavigator skillEntry = outCurrent.SelectSingleNode("my:SkillEntry", NamespaceManager);
                        XPathNavigator comment = outCurrent.SelectSingleNode("my:Comment", NamespaceManager);

                        if (skillEntry != null)
                            writer.WriteElementString("SkillEntry", ns, skillEntry.Value);

                        if (comment != null)
                            writer.WriteElementString("Comment", ns, comment.Value);

                        writer.WriteEndElement();
                    }

                    writer.Close();
                }
            }
        }

 

Publish the template again. You will be able to open the existing form that user filled in, and changed the name in the textbox. As you can see, the behaviour of the form has included the code-behind logic.

One thought on “Populate InfoPath repeating table with data from web service

Leave a comment