Showing posts with label InfoPath. Show all posts
Showing posts with label InfoPath. Show all posts

Sunday, April 7, 2013

Retrieve a row of a repeating table

Problem: You have a repeating table on an InfoPath form and want to be able to retrieve the values of fields that are located in one specific row of the repeating table.

Solution:

You can retrieve rows of a repeating table either by their position within the repeating table or by the value of a specific field within the repeating table.

1 - Create a new InfoPath form

2  - Add a Repeating Table with 2 columns ("field1" & "field2")

3 - Add a Text Box control to the view and name it contents

4 - Add a second Text Box control to the view and name it toSelect

5 - Add a Button control and add the following code to the Clicked event handler:

A) If you want by position:


Public Sub CTRL14_5_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
            ' Write your code here.
            Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator

            Dim toSelect As String = mainDS.SelectSingleNode("/my:myFields/my:toSelect", NamespaceManager).Value

            If String.IsNullOrEmpty(toSelect) Then
                toSelect = "1"
            End If

            Dim row As XPathNavigator = mainDS.SelectSingleNode("/my:myFields/my:group1/my:group2[" & toSelect & "]", NamespaceManager)

            Dim sb As New System.Text.StringBuilder()
            If row IsNot Nothing Then
                sb.Append(row.SelectSingleNode("my:field1", NamespaceManager).Value)
                sb.Append("; ")
                sb.Append(row.SelectSingleNode("my:field2", NamespaceManager).Value)
                sb.AppendLine()
            End If

            mainDS.SelectSingleNode("/my:myFields/my:Contents", NamespaceManager).SetValue(sb.ToString())


        End Sub

B) If you want by field value:

        Public Sub CTRL15_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
            ' Write your code here.
            Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator

            Dim toSelect As String = mainDS.SelectSingleNode("/my:myFields/my:toSelect", NamespaceManager).Value

            Dim row As XPathNavigator = mainDS.SelectSingleNode("/my:myFields/my:group1/my:group2[my:field1 = '" & toSelect & "']", NamespaceManager)

            Dim sb As New System.Text.StringBuilder()
            If row IsNot Nothing Then
                sb.Append(row.SelectSingleNode("my:field1", NamespaceManager).Value)
                sb.Append("; ")
                sb.Append(row.SelectSingleNode("my:field2", NamespaceManager).Value)
                sb.AppendLine()
            End If

            mainDS.SelectSingleNode("/my:myFields/my:Contents", NamespaceManager).SetValue(sb.ToString())

        End Sub


6 - Save and preview the form

If the form retrieves rows by position, enter a number in the toSelect text box and click on the button.

If the form retrieves rows by field value then enter a piece of the text that is the same as the value of one of the field1 fields in any row of the repeating table.

Loop through rows of a repeating table

Problem: You have a repeating table on an InfoPath form and want to sequentially retrieve each row of the repeating table

Solution:
You can use the XPathNodeIterator object to loop through rows of a repeating table.

1 - Create a new InfoPath form

2 - Add a Repeating Table control with 2 columns

3 - Add a Text Box control to the view of the form and name it contents. Select it's Multi-Line property.

4 - Add a Button control to the form and on it's Clicked Event Handler write:


Public Sub CTRL8_11_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
            ' Write your code here.

            Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator

            Dim rows As XPathNodeIterator = mainDS.Select("/my:myFields/my:group3/my:group4", NamespaceManager)

            Dim sb As New System.Text.StringBuilder()

            While rows.MoveNext

                sb.Append(rows.Current.SelectSingleNode("my:field4", NamespaceManager).Value)
                sb.Append("; ")
                sb.Append(rows.Current.SelectSingleNode("my:field5", NamespaceManager).Value)
                sb.AppendLine()
            End While

            mainDS.SelectSingleNode("/my:myFields/my:contents", NamespaceManager).SetValue(sb.ToString())

        End Sub


5 - Save and preview the form

When the form opens, add a couple of rows of data to the repeating table and then click on the button. The Text Box will display all data you entered into the fields in the Repeating table separated by semi-colons.

Saturday, February 2, 2013

Retrieve design information for a control on a view

Problem: You have a drop-down box list box control on an InfoPath form. The drop-down list box contains manually entered items, which means that the values and display names are part of the view the drop-down list box is located on. You want to programatically retrieve these values and display names when the form opens.

You can use the OpenFileFromPackage() method of the FormTemplate object of a form to access the XSL file that is used to create controls on a specific view

To retrieve design information for a control on a view:

1 - Create a new InfoPath template and add a Drop-Down List Box control to View1

2 - Add the manual choices to Drop-Down List Box Control:

Value: 1
Display Name: Item 1

Do the same for items 2, 3, 4 & 5.

3 - Add the following code to the event handle of the Loading event of the form


Public Sub FormEvents_Loading(ByVal sender As Object, ByVal e As LoadingEventArgs)
            ' Write your code here.

            Using stream As System.IO.Stream = Template.OpenFileFromPackage("View1.xsl")

                If stream Is Nothing Then
                    Return
                Else
                    If stream.Length = 0 Then
                        Return
                    End If
                End If

                Dim doc As XPathDocument = New XPathDocument(stream)
                Dim root As XPathNavigator = doc.CreateNavigator

                Dim dropdown As XPathNavigator = root.SelectSingleNode("//*[@xd:xctname = 'dropdown' and @xd:binding = 'my:field1']", NamespaceManager)

                Dim iter As XPathNodeIterator = dropdown.Select("//option", NamespaceManager)

                If iter IsNot Nothing Then
                    While iter.MoveNext

                        Dim value As String = String.Empty
                        Dim displayname As String = String.Empty

                        Dim valuenode As XPathNavigator = iter.Current.SelectSingleNode("@value", NamespaceManager)

                        If valuenode IsNot Nothing Then
                            value = valuenode.Value
                        End If

                        Dim iter2 As XPathNodeIterator = iter.Current.SelectChildren(XPathNodeType.Text)
                        iter2.MoveNext()
                        displayname = iter2.Current.Value

                        If displayname = "Item 4" Then
                            MainDataSource.CreateNavigator().SelectSingleNode("/my:myFields/my:field1", NamespaceManager).SetValue(value)
                        End If

                    End While

                End If
                stream.Close()
            End Using
        End Sub


4 - Save and build the project

When the form opens, Item 4 should be displayed as the selected item in the drop-down list box.

The OpenFileFromPackage() method of the FormTemplate object of a form allows you to open fiels that are stored in the form template (.xsn).

Once you have an XPathDocument object, you can use this XPathDoucment object to read the data from the XSL file and retrieve the design information for any control.

Saturday, January 26, 2013

Ignore and clear all errors in a form before submit

Problem: You want to be able to submit an InfoPath form despite any data validation errors that the form may have, so you want to ignore and delete all validation errors before submitting the form.


You can use the DeleteAll() method of the Errors collection to clear all of the errors in a form:

To Ignore and clear all of the errors in a form before submitting the form:

1 - Create a new form template

2 - Add a Textbox control to the view nad make it a required field by selecting its Cannot Be Blank Property.

3 - Go to the Submit Option under File and select "Allow users to submit this form" check box, select the "Perform custom action code option, deselect the "Show the Submit button in both the ribbon and the info tab in the InfoPath Filler". Click on Edit Code.

4 - Add following code to the FormEvents_Submit() event handler


Public Sub FormEvents_Submit(ByVal sender As Object, ByVal e As SubmitEventArgs)
            ' If the submit operation is successful, set
            ' e.CancelableArgs.Cancel = False
            ' Write your code here.
            System.IO.File.WriteAllText("C:\Temp\myform.xml", MainDataSource.CreateNavigator().OuterXml)
            e.CancelableArgs.Cancel = False
        End Sub


You will need to give the form full trust in order to save it to disk

5 - Add a button control to the view and label it "Submit". Add the following code to the clicked event handler of this button


Public Sub CTRL2_5_Clicked(ByVal sender As Object, ByVal e As ClickedEventArgs)
            ' Write your code here.

            If Me.Errors.Count > 0 Then
                Me.Errors.DeleteAll()
            End If

            Submit()

        End Sub

* The first three lines of code delete all of the errors that are in the Errors collection of the form, and the fourth line of code calls the FormEvents_Submit() event handler to submit the form.

6 - Save and build project

7 - Preview the form.

When the form opens, a red asterisk (*) should appear in the text box as an indication that it is a require field. Click the button.  The form should be submitted without warning you about any data validation errors.

Thursday, November 22, 2012

Clear a specific validation error in a form

Problem: You want to be able to clear a specific error in a form.

You can use the Delete() method of the Errors collection to clear a specific error in a form by passing either a name or a FormError object to the method.

To clear a specific error in a form by name:

1 - Create a new form template and add a Text Box control. Name the control field1

2 - Add the following code to the event handler for the Validating event of the text box field 1

                If e.Site IsNot Nothing Then
                If e.Site.Value.Length > 10 Then
                    Errors.Add(e.Site, "MaxCharsErr", "Only 10 characters max allowed.")
                End If
            End If

This code checks whether the amount of characters typed into the text box exceeds 10 and if it does it adds a user-defined error named MaxCharsErr to the Errors collection of the form.

3 - Add a second Text Box control to the form and call it "Required". Make this field required by selecting its Cannot be Blank property. The purpose of this text box is to add a schema validation error to the Errors collection of the form, so that there is more than one error in the collection for testing purposes.

3 - Add a Button to the form. Add the following code to the Clicked event of the button

            Dim errors As FormError() = Me.Errors.GetErrors("MaxCharsErr")
            If errors IsNot Nothing Then
                If errors.Length > 0 Then
                    Me.Errors.Delete("MaxCharsErr")
                End If
             End If

The first line of code retrieves all of the errors that have the name MaxCharsErr from the Errors collection. The second and third lines of code check whether any FormError objects were returned by the GetErrors() method, and if so, the Delete() method is called to delete them.

4 - Save and Preview the form

When the form opens, a red asterisk (*) should appear in the required text box as an indication that it is a required field. Enter a piece of text that is longer than 10 characters in the field1 text box. A red dashed border should appear around the text box. Click the button and the red dashed border should disappear  but the asterisks in the required text box should still be present. With this you have cleared the validation error on the field1 text box, not not on the required text box.

Saturday, October 27, 2012

Check for a specific error in a form (Error Summary)

Problem: You have a text box control on an InfoPath form and want to display a list of errors that have been raised for the data entered into the text box.

You can use the Errors property of a form to retrieve error messages for a field.

1 - Create a new form template and add two Text Boxes control to the page and name it "field2" and "Errors".

2 - Select the Text Box and select its Cannot Be Blank property.

3 - Add two Button control to the form and label them "Set User-Defined Errors" and "Get All Errors".

4 - For the Clicked() event handler of the "Set User-Defined Errors" button add:

Dim field2 As XPathNavigator = MainDataSource.CreateNavigator.SelectSingleNode("/my:myFields/my:field2", NamespaceManager)

Errors.Add(field2, "Error1", "This is error 1.")
Errors.Add(field2, "Error2", "This is error 2.")
Errors.Add(field2, "Error3", "This is error 3.")

This code should raise three user-defined errors on the text box.

5 - For the Clicked() event handler of the "Get All Errors" button add:

Dim sb As New System.Text.StringBuilder
            For Each err As FormError In Errors
                sb.AppendFormat("Form Error Type: {0} - ", err.FormErrorType)
                sb.AppendFormat("Message: {0}", err.Message)
                sb.AppendLine()
            Next

MainDataSource.CreateNavigator.SelectSingleNode("/my:myFields/my:Errors", NamespaceManager).SetValue(sb.ToString)

6 - Save and preview the form

When the form opens click the "Get All Errors" button and you should see error messages appear in the errors text box. After that click on the "Set User-Defined Errors" button and then click on the "Get All Errors" button again. You will that the three user-defined errors should appear in the errors text box.

ps. Use "err.Site.LocalName" if you would like to retrieve the name of field on which the error occurred.

The following code:

sb.AppendFormat("The field " & err.Site.LocalName & " has the following error: " & err.Message)

Renders:

The field salary has the following error: Cannot be blank

Wednesday, October 17, 2012

Validate a field when its value changes

Problem: You have a text box control on an InfoPath form and want to display an error to the user if the text the user types into the text box is longer than 10 characters.

You can use the ReportError() method of the XmlValidatingEventArgs object or the Errors property of the form to display an error message to the user validating a field.

To validate a field when its value changes:

1 - Create a new form template and a Text Box control to it

2 - Add an event handler for the Validating event of the text box control

3 - If you want to use the ReportError() method to display the error message add the following code below:


          Dim fieldVal As String = e.Site.Value
            If Not String.IsNullOrEmpty(fieldVal) Then
                If fieldVal.Length > 10 Then
                    e.ReportError(e.Site, False, "Only 10 characters max allowed.")
                End If
            End If

or if you want to use the Errors() property of the form to display an error message:


Dim err As FormError() = Me.Errors.GetErrors("MaxCharError")
            If err IsNot Nothing Then
                If err.Length > 0 Then
                    Me.Errors.Delete("MaxCharError")
                End If
            End If

            Dim fieldVal As String = e.Site.Value
            If Not String.IsNullOrEmpty(fieldVal) Then
                If fieldVal.Length > 10 Then
                    Me.Errors.Add(e.Site, "MaxCharError", "Only 10 characters max allowed.")
                End If
            End If

4 - Save and preview the form

While the behavior of the two methods for displaying an error message to a user are similar, they differ in that the ReportError() method adds a SystemGenerated error to the FormErrorCollection object of the form, while the Add() method adds a UserDefined error to the FormErrorCollection object of the form.

Sunday, October 14, 2012

Save form data to a PDF file on a disk

Problem: You want to print some or all of the data from an InfoPath form to PDF.

You can create a print view that can be used specifically to print form data to PDF and then assign this view as the print view of another view that is used to fill out the form.

1 - Create a new InfoPath Filler form

2 - Create a new view and call it PrintToPDF

3 - Create custom fields, add those fields to both the default and the PrintToPDF view.

4 - Customize the PrintToPDF view and make sure that no borders for the controls appears. One way to do this is to use "Calculated Values" as much as possible instead of regular Text Boxes. Another way is to simply hide the borders under properties.

5 - Go back to the default view, click on properties and under the Print Settings tab select PrintToPDF in the drop-down under the Designate Print View section. Once this is done you have configured the default view to use PrintToPDF view as its print view.

6 - Add a button and under it's clicked event handler add the following code"

   Dim formName As String = CurrentView.Window.Caption
   CurrentView.Export("c:\temp\" & formName & ".pdf", ExportFormat.Pdf)

7 - Give the form template Full Trust. Save and build the project.

8 - Once you click on the button you will see that a PDF was created under the temp folder.

The Export() method is not available for InfoPath browser forms. For browser forms you can use a different technique that will be shown later that saves the form as a PDF in a SharePoint document library.

Switching between different views

Problem: You would like to press a button and have the form go to a different view

1 - Create "View 1" (Default) and "View 2" on the form template

2 - On "View 1"  add a button, set the ID of this button to "ChangeView" and change the label to "Change View".

3 - On the Clicked event handler of the button "ChangeView" add the following code

ViewInfos.SwitchView("View 2")

4 - Preview the form, you will notice that when you click the button "ChangeView" the form will switch view to "View 2".

Switch to show a specific view when a form opens

Problem: You have an InfoPath form that has two views and you want to be able to set the InfoPath form to start up with and show one of these two views.

You can use the SetDefaultView() method of the LoadingEventArgs object in the FormEvents_Loading() event handler of a form to switch to a particular view when the form opens.

1 - Add a new view called "View 2"

2 - Add the following code to the FormEvents_Loading() event handler

 e.SetDefaultView("View 2")

3 - Preview the form, when it opens View 2 will appear.

Add error-handling to an InfoPath form

Problem: You have written code for an InfoPath form template and want to add error-handling to it.

You can add Try-Catch blocks to all public methods in the FormCode class and use a central error-handler to handle all errors that might take place.

To add error-handling to an InfoPath form:

1 - Add a button control to the view of the form template

2 - Add the following private method to the FormCode class

Private Sub HandleErrors(ByVal ex As Exception)

     System.Windows.Forms.MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,    MessageBoxIcon.Error)

End Sub

3 - Add the following code to the button clicked event handler

  Try
                Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator
                Dim noValue As String = mainDS.SelectSingleNode("//my:field1", NamespaceManager).Value
            Catch ex As Exception
                HandleErrors(ex)
            Finally
                'Perform any operation for clean up here
   End Try

In the code above "field1" does not exist on the main data source so after previewing the form you will get the following error message "Object reference not set to an instance of an object".

The code above works for an InfoPath Filler form but for a Browser form, modify the HandleErrors() method to log a message to the Windows Event log as follows:

Private Sub HandleErrors(ByVal ex As Exception)

            Dim source As String = "Infopath Browser Form"

            If Not System.Diagnostics.EventLog.SourceExists(source) Then
                System.Diagnostics.EventLog.CreateEventSource(source, "Application")
            End If

            Dim log As New System.Diagnostics.EventLog("Application")
            log.Source = source
            log.WriteEntry(ex.Message, System.Diagnostics.EventLogEntryType.Error)

  End Sub

If you get a request permission error when the form is trying to log the error than set the permission of the form to Full Trust and you will see the error logged on the Windows Event Log.

Monday, October 8, 2012

Setting fields to their original values

Problem: You want to restore an InfoPath form to the state it had when it was opened for the very first time.

You can save the state of a form when it initially opens and then restore to this state by replacing all the values on the fields with the data you initially stored.

The method to do this is a little different between an InfoPath Filler or browser form.

1 - Design a form and add as many fields as you like.
2 - Add a button to the form and label it RESET
3 - Add the following code to the FormCode class

For InfoPath Filler:

Private initialData As String

For Browser Forms

Private Property initialData() As Object

            Get
                Return FormState("initialData")
            End Get
            Set(ByVal value As Object)
                FormState("initialData") = value
            End Set
 End Property

4 - Add the following code to the FormEvents_Loading() event handler

initialData = MainDataSource.CreateNavigator().SelectSingleNode("/my:myFields", NamespaceManager).InnerXml

5 - Add the following code to the Clicked() event handler of the button

MainDataSource.CreateNavigator.SelectSingleNode("/my:myFields", NamespaceManager).InnerXml = initialData.ToString

Preview the form and change the default values of the fields. Notice that once you click the Reset button the value goes back to their original values.

The innerXML property of an XPathNavigator object gets or sets the markup that represents the child nodes of the current node. By using this property you can store the entire XML contents of the myFields group node in the variable or property.

Clear a field on a form

Problem: You want to clear a text box on a form.

You can set the value of the field that is bound to a text box control to be equal to an empty string to clear it.

In order to clear a text box called "Field 1" do the following:

- Add a text box field called "Field 1" and a button to the form. Set the ID of the button to "ClearTextButton"

- On the ClearTextButton_Clicked event handler add the following code

Dim dsMain As XPathNavigator = MainDataSource.CreateNavigator
Dim Field1 As XPathNavigator = dsMain.SelectSingleNode("/my:myFields/my:field1", NamespaceManager)

Field1.SetValue(String.Empty)

Set the value of a field II

Note that a few controls in InfoPath such as for example date picker controls have a nil attribute set o the fields they are bound to when they are empty. This nil attribute must be removed before you can set the value of such fields, otherwise you will get the following error when you try to set the field's value through code:

Schema validating found non-data type errors

In order to remove the nil attribute from a date picker control add the following code:

Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator
Dim StartDate As XPathNavigator = mainDS.SelectSingleNode("/my:myFields/my:StartDate", NamespaceManager)

If StartDate.MoveToAttribute("nil", NamespaceManager.LookupNamespace("xsi")) Then
    StartDate.DeleteSelf()
End If

StartDate.SetValue("2012-10-08")

The following InfoPath controls typically have nil attributes on the fields they are bound to:
  • Text Box with the Whole Number (integer) data type
  • Text Box with the Decimal (double) data type
  • Text Box with the Time (time) data type
  • Date Picker
  • Date and Time Picker
  • File Attachment
  • Picture (when a picture is included in the form instead of as a link)
  • Ink Pickture
You can find out whether a control supports the nil attribute by adding a control to the form template, previewing the form, leaving the control empty, saving the form locally on disk, opening the XML file for the form in Notepad, and then looking for:

xsi:nil="true"


Saturday, October 6, 2012

Set the value of a field I

Problem: You have a text box control on a form and you want to set its component to be equal to a piece of text.

Create a form, add two text boxes named field 1 and field 2. After that set the default value of field 1 to "This is my value".

Finally add the code below to the FormEvents_Loading() event handler

Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator

Dim Field1 As XPathNavigator = mainDS.SelectSingleNode("/my:myFields/my:field1", NamespaceManager)


Dim Field2 As XPathNavigator = mainDS.SelectSingleNode("/my:myFields/my:field2", NamespaceManager)


Field2.SetValue(Field1.Value)

Preview the form and you will see that field 2 now has the same value as field 1

Get the value of a field

Problem: You want to retrieve the contents of a text box on a form.

In InfoPath create a form and add a TextBox, name it "Field 1" and set it's default value to "Hello, this is my default value".

On the field task pane, select "Field 1" right click and select "Copy XPath".

Add the code below on FormEvents_Loading() event handler:

Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator
Dim Field1 As XPathNavigator = mainDS.SelectSingleNode("/my:myFields/my:field1", NamespaceManager)

Dim val As String = Field1.Value

Set a breakpoint on the line after the last line of code and press F5. Verify that the variable "val" has the same value as Field 1.

Access the Main & Secondary Data Source

Main Data Source
If you would like to write code that access data on an InfoPath form, the first step to access data stored within a form is to get a reference to the main source of the form.

Add the following code to the FormEvents_Loading() event handler

Dim mainDS As XPathNavigator = MainDataSource.CreateNavigator()

Secondary Data Source
If you have a form that has one or more secondary data sources associated with it and you want to access the data from one of those data sources.

In the case below you would like to access a secondary data source called "Employee" for example.

Add the following code to the FormEvents_Loading() event handler

Dim secDS As XPathNavigator = DataSources("Employee").CreateNavigator()

The DataSources property of the Xmlform object is a DataSourceCollection object, which contains DataSources Objects corresponding to each data source of the form with the Main data source of the form listed as the first data source in the colllection. You can access a DataSource object either by its position in the collection or by its name. For example:

Dim mainDS as DataSource = Datasources(0)
or
Dim mainDS as DataSource = DataSources("")
or
Dim mainDS as DataSource = MainDataSource

They all would return a DataSource object for the Main data source of a form.

Dim EmployeeDS as DataSource = DataSources(1)
or
Dim EmployeeDS as DataSource = DataSources("Employee") 

Would return a DataSource object for the Employee secondary data source.

Once you have retrieved an XPathNavigator object, you can use its methods to manipulate the data stored in the data sources.


About Me

My photo
Toronto, Ontario, Canada
MBA, M.Sc. & MCP