I have often encountered a very biting separation between repeating element controls in InfoPath, and code that manages the contents of these repeating elements.
Abstract: An InfoPath form presents the user with a search box and displays the results of the search in a meaningful way. This is used for quick “lookups” while the user is filling out the form.
We need a good way to manage the data returned from a search, as well as an elegant way to display this data to the user.
Example: Specifically, this search returns a list of “vendor” data to the user. The user enters a search term, and the code-behind methods in the form execute a search by querying against a database. Each row that is returned by the query needs to be presented to the user in a useful manner. Each row has a vendor name, code, and a few lines of the vendor’s address for billing purposes.
InfoPath gives us a few ways to display repeating data:
Decent solution: Use a listbox control
My first solution to this problem is the most straightforward because it involves simply writing values to the listbox:
- Easy to scroll through
- Easy to fill using code
- Very difficult to display LOTS of data (i.e. the vendor’s full address)
- No ability to add graphics
- Data is all text; it’s pipe-delimited, which means we must parse it out again when the user selects it
In order to fit our use case requirements, I created a repeating element in the form’s data source called “Vendor_Listbox_Item” with one child field as follows:
Manage the contents of the listbox using code
For each row returned from the database query, we’ll append an instance of this repeating element to the form’s data source. The data will reside in the “Vendor_Listbox_Item_Name” field.
//Create an XmlDocument object XmlDocument doc = new XmlDocument(); //Create Vendor_Listbox_Item element XmlNode group = doc.CreateElement("Vendor_Listbox_Item", NamespaceManager.LookupNamespace("my")); //Create Vendor_Listbox_Item_Name field, where we store the data returned from the database query XmlNode field = doc.CreateElement("Vendor_Listbox_Item_Name", NamespaceManager.LookupNamespace("my")); //Listbox "value" source XmlNode node = group.AppendChild(field); //Parse the column values returned from the database row and append them to the data field node.InnerText = reader["VENDOR_CODE"].ToString() + " | " + reader["ADDR1"].ToString() + " || \n" + reader["ADDR2"].ToString() + " ||| \n" + reader["ADDR3"].ToString() + " |||| \n" + reader["ADDR4"].ToString(); //Add the newly-created repeating element to the form's XML data source MainDataSource.CreateNavigator().SelectSingleNode("//my:myFields/my:Vendor_Listbox", NamespaceManager).AppendChild(doc.DocumentElement.CreateNavigator());
An important note: Even though we could store more data in this repeating element by adding fields (for example, by adding a field to the repeating element for every address line), remember that an InfoPath listbox can only display ONE field. That’s why we pipe-delimit the vendor address fields in order to display as much data to the user as possible.
Bind a listbox to a repeating XML element
We bind the repeating element to the listbox like this:
When the user selects an item from the listbox, four different text box controls filter through the pipes to display each line of the vendor’s address.
The implementation of this “listbox” method is shown below, with actual vendor addresses blurred. The listbox is on the left, and the four text box controls that break the vendor address into four lines is on the right:
This method of binding search results to a repeating element and binding the repeating element to a listbox is simple and easy to accomplish. However, the end product is messy. Only by selecting a vendor from the listbox can the user display the vendor’s full address in an easy-to-read format.
Stay tuned for Part 2: Using a repeating table