Greg Collins
in

InfoPath Dev

This Blog

Syndication

Greg Collins

  • Create a "Same As" Check Box

    When creating a purchase order you need to gather the user's billing information as well as their shipping information. The user might want their order shipped to their billing address, in which case their shipping address is the same as their billing address. At other times they might want their order shipped elsewhere, as in the case of purchasing a gift for someone else.

    In this task we will create the shipping/billing section of a purchase order form and focus on how to reliably allow the user to specify whether to use their billing information as the shipping information. Let's start by designing a new blank form.

    Create the schema:

    1. Open the Data Source task pane.
    2. Double-click myFields and rename it to Order.
    3. Add a Group named Billing.
    4. Add a Group named Shipping.
    5. Select the Billing group, and then add eight Text Fields named Name, Street1, Street2, City, State, PostalCode, Phone, and Email.
    6. Right-click the Name text field, choose Reference, select the Shipping group, and then click OK.
    7. Perform the previous step for each of the seven remaining text fields in the Billing group, preserving their order.
    8. Select the Shipping group and then add a True/False Attribute Field named sameAsBilling with its default value set to False.

    We have just created a schema that will allow us to gather billing and shipping information for a purchase order. Figure 1 shows the completed schema in the Data Source task pane. The shipping information contains the same fields as the billing information with the addition of a Boolean value to allow the user to indicate whether they want their order shipped to their billing address or somewhere different. Now let's create the view.


    Figure 1. The purchase order data source

    Design the view layout:

    1. Choose Font from the Format menu.
    2. In the Font task pane, select Heading 2.
    3. At the top of the view, type PURCHASE ORDER, and then press Enter.
    4. Insert a 1 x 2 Layout Table into the view.
    5. In the left cell of the layout, type Billing Information:, and then press Enter.
    6. In the Data Source task pane, right-click the Billing group, choose Controls In Layout Table, and then drag the right edge of the inserted billing layout table to use the remaining space of the containing layout table cell.
    7. In the right cell of the layout table, type Shipping Information ():, and then press Enter.
    8. In the Data Source task pane, right-click the Shipping group, choose Controls In Layout Table, and then drag the right edge of the inserted shipping layout table to use the remaining space of the containing layout table cell.
    9. Select the text Same As Billing and its associated check box, drag-and-drop it between the parentheses on the line above, and then remove the extra line breaks that were inserted during the drop.
    10. Select the two empty cells remaining from where we just moved the text and check box, and then press Delete.

    The view is now laid out, and the results can be seen in Figure 2. At this point the user can fill in their billing and shipping information, but the Same As Billing check box is non-functional. Let's add two rules on the check box such that when it is selected, the billing fields are copied to the shipping fields, and when the check box is cleared, the shipping fields are cleared.


    Figure 2. The completed purchase order layout

    Add a rule to the check box to copy the billing fields:

    1. Double-click the Same As Billing check box.
    2. In the Check Box Properties dialog box, click Rules, and then click Add.
    3. Name the rule Copy Billing Fields, and then click Set Condition.
    4. In the Condition dialog box, select True in the third drop-down list, as shown in Figure 3, and then click OK.
    5. Click Add Action.
    6. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    7. Click the Select XPath button to the right of the Field text box, select Shipping/Name, and then click OK.
    8. Click the Insert Formula button to the right of the Value text box, click Insert Field Or Group, select the Billing/Name field, and then click OK three times.
    9. Add seven more actions to this rule to copy the remaining fields from the Billing group to the Shipping group.
    10. Click OK to complete this rule.


    Figure 3. Setting the rule condition

    Add a rule to the check box to clear the shipping fields:

    1. In the Rules dialog box, click Add.
    2. Name the rule Clear Shipping Fields, and then click Set Condition.
    3. In the Condition dialog box, select False in the third drop-down list, and then click OK.
    4. Click Add Action.
    5. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    6. Click the Select XPath button to the right of the Field text box, select Shipping/Name, and then click OK twice, leaving the Value text box blank.
    7. Add seven more actions to this rule to clear the remaining fields in the Shipping group.
    8. Click OK three times to close all open dialog boxes.

    Previewing the form at this point, you will find that any values you enter into the billing fields are copied to the shipping fields when you select the Same As Billing check box, and that each of the shipping fields are cleared when you clear the check box. But there are a couple of issues that need to be resolved.

    First, when the Same As Billing check box is selected, you can still change the values in the shipping fields, effectively making the check box inaccurate. Second, when the check box is selected, any changes you make to the billing fields are not reflected in the shipping fields. Let's make the changes necessary to correct these issues.

    Prevent the user from changing the shipping fields:

    1. Double-click the Name field in the shipping column.
    2. On the Display tab of the Text Box Properties dialog box, click Conditional Formatting, and then click Add.
    3. Select sameAsBilling in the first condition drop-down list, and then select True in the third condition drop-down list.
    4. Select the Read-Only formatting check box, and then click OK three times to close all open dialog boxes.
    5. Add this same conditional formatting to each of the seven remaining shipping fields in the view.

    Ensure that the shipping fields reflect changes made to the billing fields:

    1. Double-click the Name field in the billing column.
    2. On the Data tab of the Text Box Properties dialog box, click Rules, and then click Add.
    3. Name the rule Copy Changes to Shipping Name, and then click Set Condition.
    4. Select Select A Field Or Group in the first condition drop-down list.
    5. In the Select A Field Or Group dialog box, select the Shipping/sameAsBilling field, and then click OK.
    6. Select True in the third condition drop-down list, and then click OK.
    7. Click Add Action.
    8. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    9. Click the Select XPath button to the right of the Field text box, select Shipping/Name, and then click OK.
    10. Click the Insert Formula button to the right of the Value text box, click Insert Field Or Group, select the Billing/Name field, and then click OK six times to close all open dialog boxes.
    11. Add similar rules, naming them appropriately, to each of the seven remaining billing fields in the view.

    With these last two changes our billing/shipping section is complete. The user can enter separate billing and shipping information or choose to have their shipping information be the same as their billing information by selecting the Same As Billing check box. If the user selects the latter option, the shipping data will accurately reflect the most current billing information, even if changes are made to the billing information.

    Try it:

    Preview the form and test your new billing/shipping section with and without the Same As Billing check box selected. The completed form is shown in Figure 4.


    Figure 4. The completed purchase order billing/shipping section

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Remove Text Highlighting

    When using the text highlighter on the contents of a rich text field, or on the contents of a view in the designer, InfoPath insert a font element around the text being highlighted, setting the background color to the selected highlight color.

    The following code block shows the XHTML of two paragraphs in a rich text field. The first paragraph has no highlight, while the second one has a yellow highlight.

    <div xmlns="http://www.w3.org/1999/xhtml">This text has no highlight.</div>
    <div xmlns="http://www.w3.org/1999/xhtml"><font style='BACKGROUND-COLOR: #ffff00'>This text has a yellow highlight.</font></div>

    Adding a highlight is as simple as selecting text and then choosing the highlight color; but removing the highlight is not as obvious. Microsoft Word has a very definite choice of None among its highlight colors (as shown in Figure 1) while InfoPath does not.


    Figure 1. InfoPath (left) provides more highlight colors than Word (right), but does not provide the option for ‘None’.

    Although not obvious, InfoPath also provides a None choice: the white highlight color. If you select text with a highlight color and then choose the white highlight color, InfoPath will remove the font element from the XHTML contents of the rich text field. Our previous code block would now look like this:

    <div xmlns="http://www.w3.org/1999/xhtml">This text has no highlight.</div>
    <div xmlns="http://www.w3.org/1999/xhtml">This text has a yellow highlight.</div>

    But there is a catch. The white highlight color will only remove the highlight when the entire highlighted section is selected, otherwise it will insert a font element to set a white background color. For example, if instead of selecting the entire second paragraph and setting the highlight color to white, we only select a portion of the highlighted text (whether that be at the start, middle, or end of the paragraph), the resulting XHTML will look something like this:

    <div xmlns="http://www.w3.org/1999/xhtml">This text has no highlight.</div>
    <div xmlns="http://www.w3.org/1999/xhtml"><font style='BACKGROUND-COLOR: #ffff00'>This <font style='BACKGROUND-COLOR: #ffffff'>text has</font> a yellow highlight.</font></div> </my:field1>

    The results of this behavior would be more obvious if the background color of the control or view were set to something other than the default white color.

    So how do you completely remove the highlight color from a portion of the highlighted text rather than change it to white? The solution is to use another less obvious option, which is the keyboard shortcut for removing text formatting. To use the keyboard shortcut, select the desired text, and then press Ctrl+Space to remove the formatting. Although this works, if you had other formatting besides just the highlight color, you might end up with unexpected results. This approach is fully described in Quickly Remove Text Formatting.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Disable a Button Until All Required Fields are Filled In

    You might have a button in your form—such as a submit button—that you want to prevent the user from being able to click until all required form fields are filled in. This can be done using conditional formatting. The complexity of the conditional formatting depends on your form.

    In this task we will create a simple form with a submit button. We will add conditional formatting to disable the button until all required form fields are filled in. Let’s start by designing a new blank form.

    Create the schema:

    1. Open the Data Source task pane.
    2. Add a Text Field named Name, which cannot be blank.
    3. Add two Text Fields named Address, and Phone.
    4. Add a Text Field named Email, which cannot be blank.
    5. Add a Rich Text Field named Comments.

    Add custom data validation to require that the rich text field cannot be blank:

    Since we cannot specify in the data source that the rich text field cannot be blank, we must do this manually with custom data validation. Rich text fields work differently than plain text fields—you cannot simply check whether a rich text field “is blank” because this will not always produce accurate results.

    1. In the Data Source task pane, double-click Comments.
    2. On the Validation And Event Handlers tab of the Field Or Group Properties dialog box, click Add.
    3. Select The Expression from the first drop-down list, and then type the following expression:

    not(.//node())

    1. Type Cannot be blank for the ScreenTip (as shown in Figure 1), and then click OK twice.


    Figure 1. Custom validation requiring that a rich text field cannot be blank.

    Design the view:

    1. Right-click myFields, and then choose Controls In Layout Table.
    2. From the Controls task pane, insert a Button into the view.
    3. Double-click the button.
    4. On the General tab of the Button Properties dialog box, change the label to Submit, and then change the ID to btnSubmit.
    5. Click Rules, and then click Add.
    6. Name the rule Submit, and then click Add Action.
    7. Select Show A Dialog Box Message from the Action drop-down list.
    8. Type Form Submitted for the message, and then click OK three times.

    Add conditional formatting to disable the button:

    1. On the Display tab of the Button Properties dialog box, click Conditional Formatting, and then click Add.
    2. Select Name from the first drop-down list, select Is Blank from the second drop-down list, click And, and then select Or in place of And.
    3. Select Email from the first drop-down list, select Is Blank from the second drop-down list, click And, and then select Or in place of And.
    4. Select The Expression from the first drop-down list, and then type the following expression:

    not(my:Comments//node())

    1. Select the Disable This Control check box (as shown in Figure 2), and then click OK three times.


    Figure 2. The conditions required to disable the button.

    Try it:

    When you first preview the form you will find that the Name, Email and Comments fields are all marked with a red asterisks, indicating that they are required fields; you will also find that the Submit button is disabled. When you fill in the required fields the Submit button becomes enabled, allowing you to then submit the form. Figure 3 shows the form before and after the required fields are filled in.


    Figure 3. The submit button is disabled until all required fields are filled in.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Prevent Field Validation From Occurring OnLoad

    The OnValidate event fires for each field in your main DOM before the OnLoad event is fired. This allows you to write validation code to add custom errors to the Errors collection for invalid fields before the user ever sees the view. This might be necessary when you are unsure of the source of the XML and for numerous other reasons. Although very useful, you might have validation event handlers that you do not want processed at this point. It is possible to prevent these validation event handlers from being processed.

    Because the View object does not exist until the first OnSwitchView event, which occurs after the OnLoad event, you can use this to detect whether your validation event handler is being called before the OnLoad event. To skip this initial OnValidate event, add the following code to the beginning of your validation event handler:

    if(null == eventObj.XDocument.View)
        return;  // Ignore OnValidate before the OnLoad event.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Keep the Form Aligned with the Background Picture When Printing

    If you design your form to strictly layout over a background picture, you will find that when you print or use print preview your form no longer aligns with the background picture. Using a grid background picture, Figure 1 shows the extent of the layout variation between the editing and printing.


    Figure 1. Forms align to a background picture differently for printing.

    InfoPath requires a 21px left margin in the view to allow for the widget icon that appears when you hover over repeating controls. This margin is not required for printing. Each view .xsl file contains an internal style sheet (shown in the following code block) which uses the @media screen at-rule to specify that the media for the contained set of styles applies to computer display screens only—not for other media types such as print.

    <style controlStyle='controlStyle'>
        @media screen {
            BODY { margin-left:21px; background-position:21px 0px; }
        }
        . . .
    </style>

    The style contained in the media at-rule specifies that the background picture be offset the same 21px as the body contents. When you print, both the body margin and the background position will revert to their default values because the media at-rule no longer applies. One problem is that InfoPath places an inline style on the body element, which overrides this at-rule, as seen here:

    <body style='BACKGROUND-POSITION: left top; BACKGROUND-REPEAT: no-repeat' background='backgroundPicture.jpg'>

    When the background-repeat property has a value of no-repeat, the background-position property is added to the inline style. This property overrides the at-rule style. By setting your background picture to tile horizontally, vertically, or both, as shown in Figure 2, the background-position property is removed, thus allowing the at-rule style to be applied. But even tiling your background picture, there will be a misalignment between editing and printing because the at-rule specifies a value of zero for the top background position.


    Figure 2. Setting the background picture to tile horizontally and vertically.

    There are two approaches to matching the alignment of the form contents to the background picture between editing and printing. Each approach will do the trick, but will result in different alignment as can be seen in Figure 3 and Figure 4. The first approach is to modify the standard InfoPath internal style sheets and the inline style on the body element. This approach has some drawbacks—it works well for when you are finished modifying your form template, but you chance losing your modifications if you make them before you are finished. The first approach also explains why this problem exists. The second approach is simpler and will survive modifications to the form template. Let’s review the two approaches.


    APPROACH 1 – MODIFY THE STANDARD INTERNAL STYLE SHEETS AND INLINE STYLES

    You can manually remove the background-position property from the inline style on the body element, but even this will not fix all of the issues. The first problem is that if you ever make changes in the View Properties dialog box, InfoPath will update the inline style, thus reverting your changes. The second problem is that this only fixes the left position. Referring to Figure 1, you can see that both the left and top positions are different between editing and printing. This can be corrected by adjusting the top position in the at-rule style as follows:

    <style controlStyle='controlStyle'>
        @media screen {
            BODY { margin-left:21px; background-position:21px 15px; }
        }
        . . .
    </style>

    If you now load your form in the editor the background picture will have the same alignment as it does when you print. But there’s still a problem; in the designer the top position did not get adjusted. One more addition to a different internal style will fix that. Paste a copy of the at-rule into the style block with the tableEditor attribute. Your three modifications to the view .xsl file are as follows:

    <style controlStyle='controlStyle'>
        @media screen {
            BODY { margin-left:21px;background-position:21px 15px; }
        }
        . . .
    </style>


    <style tableEditor="TableStyleRulesID">
        @media screen {
            BODY { margin-left:21px; background-position:21px 15px; }
        }
        . . .
    </style>


    <body style='BACKGROUND-REPEAT: no-repeat' background='backgroundPicture.jpg'>

    With these three modifications in place, your background picture will align the same in the designer, the editor, and for print. The results can be seen in Figure 3 below.


    Figure 3. Following Approach 1, the form aligns to the upper left of the background picture.

    This is all great and good so long as you do not make any changes to your form template that would require you to make these changes again. InfoPath often regenerates the standard internal style sheets as well as the inline style on the body element. Constantly battling the designer to keep your modifications is not a pleasant task, and there is an alternative.


    APPROACH 2 – ADD A CUSTOM INTERNAL STYLE FOR PRINT MEDIA

    The alternative is to leave the standard internal style sheets and the inline style alone and to create your own internal style sheet which will adjust the margins of the body contents over the background picture for print. You do this by adding the following the following code just below the last style element in the view .xsl file:

    <style title="BackgroundPicturePosition">
        @media Print {
            BODY{ margin-left:21px; margin-top:15px; }
        }
    </style>

    By adding your own at-rule for print media, you overcome the InfoPath designer roundtripping issue. The results are shown in Figure 4. Either way will work, but the latter alternative is safer and less work. For more details on internal style sheets, refer to Add a Custom Style Sheet to a View.


    Figure 4. Following Approach 2, the form aligns to the background picture with margins.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Display Rich Content in an Expression Box

    When you bind an Expression Box to a Rich Text field you might be surprised to find that all of the formatting and rich content are gone, and that you are left with a single-line string of plain text. Getting the rich content to display requires only a small manual modification to the view .xsl file.

    In this task we will create an Expression Box bound to a Rich Text field that displays with rich content. We will also look into an issue you might experience as a result of this modification and what can be done about it. Let's start by designing a new blank form.

    Create a Rich Text Box and bind an Expression Box to it:

    1. Open the Controls task pane.
    2. Insert a Rich Text Box into the view.
    3. In the Data Source task pane, right-click field1, and then choose Expression Box.
    4. Resize the Expression Box to be the same width as the Rich Text Box above it.

    Try it:

    1. Preview the form.
    2. Add some rich content to the Rich Text field, and then click outside of the field to bind the content.


    Figure 1. An Expression Box bound to a Rich Text field does not display rich content.

    As you can see in Figure 1, the rich content is not displayed in the Expression Box. This is because InfoPath does not consider the data type of the bound field when creating an Expression Box but instead always uses the xsl:value-of element in the XSL. The xsl:value-of element converts the results of its selection to a string by calling the string() function. To allow for rich content, we must change to use the xsl:copy-of element.

    Modify the view .xsl file to allow the Expression Box to display rich content:

    1. Choose Extract Form Files from the File menu.
    2. Select a location to save your extracted form files to, and then click OK.
    3. Close InfoPath to release the lock it places on your form files.
    4. Using a text editor, open your view .xsl file.
    5. Search for the text ExpressionBox to locate the span element that represents your Expression Box control.
    6. Change the following line from:

    <xsl:value-of select="my:field1"/>

    to:

    <xsl:copy-of select="my:field1/node()"/>

    1. Save the view .xsl file, and then close the text editor.
    2. Reopen your form template by right-clicking the manifest.xsf file and choosing Design.

    Try it:

    1. Preview the form.
    2. Add some rich content to the Rich Text field, and then click outside of the field to bind the content.


    Figure 2. The modified XSL allows the Expression Box to display rich content.

    Figure 2 shows that the modification we just made to the XSL allows the Expression Box to display rich content. Something you might have noticed was that if you hover over the contents of the Expression Box, a blue border appears around the field. This is uncharacteristic of an expression box, and usually an undesirable effect.

    There is an issue in InfoPath that causes this blue border to appear when hovering over an Expression Box modified to display rich content when a Rich Text Box bound to the same field is in the same view. To prevent this blue border from appearing on hover, you must ensure that the Expression Box does not appear in the same view as a Rich Text Box. If the two controls are in separate views, this hover effect will not occur.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Using XPath's 'preceding-sibling' Axis Correctly

    The XPath axis, preceding-sibling, can be a bit confusing to use. It doesn't work the way one would expect at all. In fact, it is quite backward from what one would expect.

    To me it made sense that if I wanted the sibling just before where I was currently at I would use an XPath like this:

    preceding-sibling::NodeName[last()]

    That made sense to me... but it didn't work. After some digging around I discovered that preceding-sibling works in a reverse direction. If I were to diagram it out, using an 'X' to represent the current location, it would look like this:

    preceding-sibling

     current() 

    following-sibling

    last()

    ...

    2

    1

    X

    1

    2

    ...

    last()

    Thus, using last(), I would really end up with the first one in the list (as we humans think). So if I want access to the immediate preceding-sibling, I would instead use an XPath like this:

    preceding-sibling::NodeName[1]

    This would correctly provide me with the immediate preceding-sibling (this technique is demonstrated in the InfoPathDev example form: Reordering Table Rows). I could increase the number used if I wanted to walk further back the chain.

    So in essence, as you go forward (following-sibling) or backward (preceding-sibling), the number of steps you take is based off of where you currently are, with last() being the furthest possible away from where you are, in the direction you are going.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Prevent Button Borders from Appearing Bold

    You might encounter a situation where a button in your view appears to have a bold border, as shown in Figure 1. Traditionally in the Microsoft Windows operating system, a bold border designates a default button—meaning it is the one that is activated by default when you press Enter assuming you haven't changed the focus to another button. This is not the case here, as InfoPath does not utilize default buttons.

    The issue stems from a design flaw in the rendering of the Windows XP style button control, and does not occur in the Windows Classic style. When the width of the Windows XP style button reaches 192px, the border renders differently, making it appear bold. This is also true if the height of the button reaches 87px.


    Figure 1. Button borders appear bold when at widths greater than 191px.

    If this is visual artifact is an issue for you then you need to do what it takes to keep the width of the button below 192px, and the height of the button below 87px. This might mean modifying the button label text, or manually sizing the button.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Display Inline Pictures From a Rich Text Field on a Web Page

    To display your InfoPath form data in a Web page, you can write XSL to translate your form into HTML. With a little knowledge of XSL this is a straightforward task. To display the contents of a rich text field you must use xsl:copy-of instead of xsl:value-of. This allows the XHTML content to be displayed in the Web page.

    But you will quickly discover that this works for everything in a rich text field except pictures. This is because InfoPath stores pictures using base64 encoding, and then embeds the resulting string directly into the XML content—specifically into the xd:inline attribute of the img element. The XHTML for a picture in a rich text field will look something like the following:

    <img style='WIDTH: 100px; HEIGHT: 122px' tabIndex='-1' src='msoinline/a0d1eb893d744127' xmlns:xd='http://schemas.microsoft.com/office/infopath/2003' xd:inline='/9j/4AAQSkZJRgABAQEASABIAAD...'/>

    The src attribute tells InfoPath that the encoded picture is stored inline in the xd:inline attribute. But Internet Explorer does not understand the contents of the src attribute and ignores the xd:inline attribute, thus failing to display the picture. Because xsl:copy-of performs a single operation copy of the contents of the node, there is no way to handle the pictures independently. For this we must write our own "copy-of" code. Displaying inline pictures in your Web page requires extra work, but the results are worth it.

    In this task we will create a simple InfoPath form with a rich text field that contains inline pictures, and then display the decoded pictures in a Web page. We will use XSL to transform our XML form into HTML, and then use an ASPX page to make the base64 encoded pictures displayable in the Web page. This task requires that you have available to you an installation of Internet Information Services (IIS), version 6.0 or later, and will assume that you have it installed on your local machine. Let's start by designing a new blank form, and then we will create the several support files needed to complete this task.


    THE FORM TEMPLATE

    Create the test form template:

    1. Open the Controls task pane.
    2. Insert a Rich Text Box into the view.
    3. Double-click the rich text field.
    4. In the Rich Text Box Properties dialog box, change the field name to RichTextField, and then click OK.
    5. Save the form template as InlinePictures2Html.xsn, and then close InfoPath.


    THE MYFORM.XML FILE

    Create the test form:

    1. Launch the InlinePictures2Html.xsn form.
    2. Insert several pictures into the rich text field.
    3. Save the form as C:\Inetpub\wwwroot\form\myForm.xml, and then close InfoPath.


    THE DEFAULT.ASPX FILE

    The default.aspx file is the landing page for the Web user. This page initiates the XSL transformation our InfoPath form into an HTML Web page. This is the first of three key files required to display inline pictures from a rich text field in a Web page. Copy the following code into a text editor, and then save the file as C:\Inetpub\wwwroot\form\default.aspx. Note that this is the same folder that you saved the myForm.xml file to.

    <%@ Page Language="C#" %>
    <%@ Import Namespace="System.Xml" %>
    <%@ Import Namespace="System.Xml.Xsl" %>

    <!-- Source code developed by Greg Collins -->

    <script language="C#" runat="server">
    void Page_Load(Object sender, EventArgs e)
    {
        // Create an XSL argument to provide the absolute URL to the XML file being transformed.
        // This argument will be availabe as an xsl:param named URL.
        XsltArgumentList oArgs = new XsltArgumentList();
        string sPath = Request.Path.ToString();
        sPath = sPath.Substring(0, sPath.LastIndexOf("/")+1) + "myForm.xml";
        oArgs.AddParam("URL", "", sPath);
        xmlFile.TransformArgumentList = oArgs;
    }
    </script>

    <asp:Xml id="xmlFile" runat="server" DocumentSource="myForm.xml" TransformSource="/transform.xsl" />

    Let's examine what the default.aspx file is actually doing. If we were not attempting to decode inline pictures from a rich text field the entire script block would be unnecessary. The purpose of the script block is to get the path from the root folder to the XML file to be transformed, and then pass it as a parameter to the XSL. We are using code to identify this path but you could just as easily hardcode it.

    Because Request.Path actually returns the path to the default.aspx file, we must remove the filename portion of the path and replace it with the filename of the XML file to be transformed. We could use Server.MapPath("myForm.xml") here, but this might pose a security issue due to the physical directory structure it reveals; instead we will save that for the decodePicture.aspx file. The modified path becomes the value of a parameter named URL, which is passed to and used in the transform.xsl file.

    Values from two attribute on the asp:Xml element must be referenced in the script block: the id attribute and the DocumentSource attribute.  The value of the id attribute is the name of the object whose TransformArgumentList property is being set at the end of the script block. If these two do not match an error will occur. The value of the DocumentSource attribute is the name of the XML file to be transformed and is appended to the path after removing the name of this ASPX file. If these two do not match you will receive undesirable results in your Web page.


    THE TRANSFORM.XSL FILE

    The transform.xsl file is what transforms our InfoPath form into the HTML that will be displayed in a Web page. This is the second of three key files required to display inline pictures from a rich text field in a Web page. It employs custom "copy-of" functionality to process img elements independently of other XHTML elements within the rich text field. This separation is important in order to specify that the source of the picture will be supplied by an ASPX page.

    You must ensure that any namespaces used in your form are also included in the namespace declarations on the xsl:stylesheet element. If you fail to include a namespace the XSL will not be processed.

    Copy the following code into a text editor, and then save the file as C:\Inetpub\wwwroot\transform.xsl. This file is saved to the root folder so that it can be easily accessed from anywhere in your Web site.

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:cs="C#" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2004-11-03T18:01:22">
    <xsl:strip-space elements="*" />
    <xsl:output method="html" indent="no" />

    <!-- Source code developed by Greg Collins -->

    <!-- Parameter specifying the absolute URL of the XML file being transformed -->
    <xsl:param name="URL"/>

    <xsl:template match="/">
        <h1>Rich Text Pictures Displayed in a Web Page:</h1>
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="my:RichTextField">
        <xsl:apply-templates />
    </xsl:template>

    <!-- Template for every element except pictures -->
    <xsl:template match="my:RichTextField//*[name(.) != 'img']">
        <xsl:element name="{name(.)}">
            <xsl:for-each select="@*">
                <xsl:attribute name="{name(.)}"><xsl:value-of select="."/></xsl:attribute>
            </xsl:for-each>
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <!-- Template specifically for pictures -->
    <xsl:template match="my:RichTextField//*[name(.) = 'img']">
        <xsl:element name="img">

            <!-- Copy every attribute except for 'src' and 'xd:inline' -->
            <xsl:for-each select="@*[name(.) != 'src' and name(.) != 'xd:inline']">
                <xsl:attribute name="{name(.)}"><xsl:value-of select="."/></xsl:attribute>
            </xsl:for-each>

            <!-- Generate the 'src' attribute to decode the base64 picture -->
            <xsl:attribute name="src">/decodePicture.aspx?u=<xsl:value-of select="$URL"/>&amp;n=<xsl:value-of select="count(preceding::*[name(.) = 'img'])"/></xsl:attribute>
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    </xsl:stylesheet>

    Let's examine what the transform.xsl file is actually doing. The xsl:param element receives its value from the parameter set by the default.aspx page that initiated the transformation. This value is the path from the root folder to the XML file being transformed. This parameter is essential because it allows the code in decodePicture.aspx to load that XML file and decode each of its base64 encoded pictures.

    The script block is used to create an index of the inline pictures. Each picture is assigned an incrementally larger number starting from zero. The code in decodePicture.aspx uses these numbers to know which picture is being requested.

    Because our sample form is so simplistic, the main template only places a header on the page. The real work is done when the template for the rich text field is processed. If pictures were not saved inline, you would simply be able to use an xsl:copy-of element here and be done with it; but because we are dealing with inline pictures, we must write custom "copy-of" functionality. This requires two templates: one for the img element, and one for everything else.

    The first of our custom "copy-of" templates matches the XPath my:RichTextField//*[name(.) != 'img'], or in other words, any element inside the rich text field that is not named img. In this template we copy the element, giving it the same name, copy each of its attributes, and then continue applying templates.

    The second of our custom "copy-of" templates matches the XPath my:RichTextField//*[name(.) = 'img'], or in other words, any element inside the rich text field, as long as it is named img. This template is very similar to the previous one, creating an element of the same name, and then copying the attributes; only this time we specifically do not copy  two of the attributes: src and xd:inline. The src attribute will be constructed later and the xd:inline attribute will be left for the decodePicture.aspx page to work with. It is this latter attribute that actually contains the base64 encoded picture.

    The reason we wrote custom "copy-of" functionality was to set up the src attribute of the img element so that it will call the decodePicture.aspx page to request the decoded picture. To do this we provide two parameters representing the URL to the XML file we are currently transforming and an index to the picture we are currently working on. The resulting img element, transformed for our HTML Web page, will look something like the following:

    <img style='WIDTH: 100px; HEIGHT: 122px' tabIndex='-1' src='/decodePicture.aspx?u=/myForm.xml&amp;n=0'>

    The contents of this transform.xsl file are the barebones requirements to ensure that your rich text pictures will be displayed. Beyond this you would write your XSL to transform the remainder of your InfoPath form into HTML for your Web page.


    THE DECODEPICTURE.ASPX FILE

    The decodePicture.aspx file is where we decode the base64 encoded inline pictures. This is the third of three key files required to display inline pictures from a rich text field in a Web page. This file gets called by the Web browser when it encounters the src attribute of the img element that we created as part of the XSL transformation.

    Copy the following code into a text editor, and then save the file as C:\Inetpub\wwwroot\decodePicture.aspx. This file is saved to the root folder so that it can be easily accessed from anywhere in your Web site.

    <%@ Page Language="C#" Debug="true" %>
    <%@ Import Namespace="System.IO" %>
    <%@ Import Namespace="System.Xml" %>
    <%@ Import Namespace="System.Collections" %>

    <!-- Source code developed by Greg Collins -->

    <script language="C#" runat="server">
    void Page_Load(Object sender, EventArgs e)   
    {
        try
        {
            // Get the parameters.
            string sXmlFile = Request.QueryString["u"];
            int nPictureNumber = Convert.ToInt32(Request.QueryString["n"]) * 2;
            if(null == sXmlFile || 0 > nPictureNumber)
                return;
       
            // Get the server path to the XML file.
            sXmlFile = Server.MapPath(sXmlFile);
       
            // Attempt to get the decoded pictures from the cache.
            ArrayList arrPicture = (ArrayList)Cache[sXmlFile];
            System.Drawing.Image oPicture;
       
            // If not already there, decode the pictures and add them to the cache.
            if(null == arrPicture)
            {
                // Load the XML file that was transformed.
                XmlDocument oXml = new XmlDocument();
                oXml.Load(sXmlFile);
           
                // Add the appropriate namespaces so the selection will work.
                XmlNamespaceManager oNsm = new XmlNamespaceManager(oXml.NameTable);
                oNsm.AddNamespace("rt", "http://www.w3.org/1999/xhtml");
                oNsm.AddNamespace("xd", "http://schemas.microsoft.com/office/infopath/2003");
           
                // Get the full list of xd:inline attributes on img elements.
                XmlNodeList oPictures = oXml.SelectNodes("//rt:img/@xd:inline", oNsm);
                arrPicture = new ArrayList();
                for(int i = 0; i < oPictures.Count; i++)
                {
                    // Convert each base64 encoded string into an actual picture.
                    string sBase64Picture = oPictures.Item(i).Value;
                    byte[] bPicture = Convert.FromBase64String(sBase64Picture);
                    MemoryStream oMemStr = new MemoryStream();
                    oMemStr.Write(bPicture, 0, bPicture.Length);
                    oPicture = System.Drawing.Image.FromStream(oMemStr);
                   
                    // Add each decoded picture and it's format to the array.
                    arrPicture.Add(oPicture);
                    arrPicture.Add(sBase64Picture.Substring(0, 4));
                }
           
                // Store the picture array in the Application Cache.
                Cache.Insert(sXmlFile, arrPicture, new CacheDependency(sXmlFile));
            }
       
            // Ignore request if the picture number excedes what's in the array.
            if(arrPicture.Count <= nPictureNumber)
                return;
       
            // Obtain the specified picture and format.
            oPicture = (System.Drawing.Image)arrPicture[nPictureNumber];
            string sPictureFormat = (string)arrPicture[nPictureNumber+1];
       
            // Send the decoded picture back to the Web page to be displayed.
            // R0lG = Uppercase R + Number Zero + Lowercase L + Uppercase G
            if("R0lG" == sPictureFormat)
                oPicture.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Gif);
            else
                oPicture.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
        }
        catch(Exception ex)
        {
            // If anything goes wrong do not display an error, just return with no picture.
            return;
        }
    }
    </script>

    Let's examine what the decodePicture.aspx file is actually doing. Two query string parameters are retrieved from the src attribute of the img element produced by our custom XSL transform: the path from the root folder to the XML file containing the inline pictures, and the index of the particular picture to decode. The index is immediately multiplied by two because the array created to store the decoded pictures also stores a sibling piece of information that identifies the picture format. If either parameter is not present, we just return, which results in a broken picture in the Web page.

    The sXmlFile variable is immediately converted to the absolute server path using Server.MapPath(sXmlFile). This variable is used to load the XML file, and is also used as the name of the object we store in the cache.

    The first thing we do is discover whether this particular XML file has already been parsed and stored in the cache; if so we use the cached version. Caching our pictures significantly improves performance because we do not need to load the XML file each time a decoded picture is requested. If not already in the cache, we must take the time decode the pictures, and then add them to the cache.

    To do this we must load the XML file, and then add the namespaces required to be able to reach the pictures in the rich text fields. Two namespaces in particular need to be added: the first is for the rich text field, and the second is for the xd:inline attribute of the img elements. After we've added the namespaces, we get the list base64 encoded pictures, and then one-by-one, decode them.

    Each decoded picture is stored in an array along with the four characters at the beginning of the base64 encoded picture. These four characters identify the picture format, whether it is GIF or JPEG. We must determine the picture format because JPEG pictures displayed in GIF format do not look as good, and GIF pictures displayed in JPEG format do not allow for animated pictures. The four characters representing a GIF picture are R0lG. The four characters representing a JPEG picture are /9j/.

    Although InfoPath supports more picture formats than GIF and JPEG, for our purposes, these are the only formats that exist. This is because all the other picture formats display correctly in one of these two formats.

    With all of these files in place you will be able to correctly display inline pictures from a rich text field in your Web pages. You can test this by launching your Web browser and navigating to http://localhost/form/default.aspx.

    One thing to note is that decoded inline pictures will not render as quickly as those loaded from actual picture files stored on a server; but this is a minor issue compared to the convenience of being able to display the complete contents of your rich text fields in a Web page, including the inline pictures.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Launch a Supporting Form

    InfoPath forms can do a lot of things. But at times you might want one form to launch another form to perform a different, but related task. You might have needs for numerous forms to work together as part of an overall solution.

    In this task we will create several buttons which will launch a supporting form using the New, NewFromSolution, and NewFromSolutionWithData methods of the XDocuments collection. Each method requires an absolute path to the supporting form or document. Since our support form and source form as well as any documents created will all be stored in the same folder, we will grab the path from the XDocument.Solution.URI property. This will allow us to relocate our forms and templates and not need to update the code. Let's start by designing a new blank supporting form.


    THE SUPPORTING FORM

    Create the supporting form:

    1. Type Supporting Form at the top of the view, and then press Enter.
    2. From the Controls task pane, insert a text box.
    3. Choose Save from the File menu, and then click Save.
    4. Create a folder named MultiForm.
    5. Name your file Supporting.xsn, and then click Save.
    6. Close InfoPath.

    Create a test document using the supporting form:

    1. Launch your Supporting.xsn file from the MultiForm folder.
    2. Type Test Data into the text box.
    3. Save your form as Form1.xml in the MultiForm folder.
    4. Close InfoPath.

    Now that we have created our supporting form and a test document, lets create the a source form which will be used to launch the supporting form. Again, let's start by designing a new blank form.


    THE SOURCE FORM

    Create the source form:

    1. Type Source Form at the top of the view, and then press Enter.
    2. From the Controls task pane, insert three buttons.

    Identify the absolute path:

    1. Choose Programming | On Load Event from the Tools menu.
    2. Add the sFormPath variable declaration just above and outside of the OnLoad event handler so that it is globally accessible to the other functions:

    var sFormPath = "";

    1. Add the following code to the OnLoad event handler:

    var sUri = XDocument.Solution.URI;
    var i = sUri.lastIndexOf("\\");
    if(-1 == i)
        i = sUri.lastIndexOf("/");
    sFormPath = sUri.substring(0, i + 1);

    1. Switch back to the InfoPath designer.

    Add code behind the New button:

    1. Double-click the first button, change the label to New, change the ID to btnNew, and then click Edit Form Code.
    2. Add the following code to the btnNew OnClick event handler:

    Application.XDocuments.New(sFormPath + "Form1.xml");

    1. Switch back to the InfoPath designer.

    Add code behind the NewFromSolution button:

    1. Double-click the second button, change the label to NewFromSolution, change the ID to btnNewFromSolution, and then click Edit Form Code.
    2. Add the following code to the btnNewFromSolution OnClick event handler:

    Application.XDocuments.NewFromSolution(sFormPath + "Supporting.xsn");

    1. Switch back to the InfoPath designer.

    Add code behind the NewFromSolutionWithData button:

    1. Double-click the third button, change the label to NewFromSolutionWithData, change the ID to btnNewFromSolutionWithData, and then click Edit Form Code.
    2. Add the following code to the btnNewFromSolutionWithData OnClick event handler:

    Application.XDocuments.NewFromSolutionWithData(sFormPath + "Form1.xml", sFormPath + "Supporting.xsn");

    Save the form:

    1. Save your code and close the script editor.
    2. In the InfoPath designer, choose Save from the File menu, and then click Save.
    3. Navigate to the MultiForm folder.
    4. Name your file Source.xsn, and then click Save.
    5. Close InfoPath.

    Try it:

    Now that we have created the source and supporting templates and a supporting test document, let's try out the various methods. Preview the form and click each of the buttons to see what happens.

    One thing you'll probably notice is that when you click either the New or NewFromSolutionWithData button for the first time you will be presented with a security warning dialog box as shown in Figure 1. To learn of ways to prevent or disable this dialog box, refer to Prevent the Internet Explorer Cross-Domain Security Dialog Box.


    Figure 1. The Internet Explorer cross-domain data access security risk dialog box.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Switch Views via Code and Rules

    There will be times when you will want to switch views during unsupported events, such as the OnAfterChange event. Due to technical reasons in the underlying platform, InfoPath only allows you to switch views during the OnLoad event and the OnClick event handlers for buttons in the form or on toolbars. The Switch View rule action is only available on buttons.

    Although there are many reasons to want to switch views during other events, InfoPath will not allow these. If you attempt to switch views during an unsupported event, you will receive an error dialog box, as shown in Figure 1.

    Figure 1. The error dialog box for an invalid view switch attempt.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Prevent the Internet Explorer Cross-Domain Security Dialog Box

    With default settings, Internet Explorer might warn you when your InfoPath form attempts to access data across domains (see Figure 1). Notice that the title bar is labeled as "Internet Explorer"; this is because Internet Explorer, and not InfoPath, is posing the security risk. Although this is beneficial for security purposes, if you are working in a controlled environment this dialog box can become annoying.


    Figure 1. The Internet Explorer cross-domain data access security risk dialog box.

    For example, the first time you use either the New or NewFromSolutionWithData methods you will be presented with the security warning dialog box shown in Figure 1. If you click Yes, the method will proceed as expected. If you click No, you will receive an error dialog box informing you that permission was denied, and the method will fail. This warning dialog box is only presented once during the life of your form. Thereafter your choice will be reused by default. To select a new choice you will need to close and reopen your form.

    This dialog box might not appear if you publish your form to a Web server. You can prevent this dialog box by setting your form template to full trust or you can disable it.

    In this task we will disable the Internet Explorer security risk dialog box by modifying your Internet Explorer security settings. This will prevent this dialog box from appearing only on the specific machine with the modified settings.

    Disable the Internet Explorer security risk dialog box:

    1. From Internet Explorer, choose Internet Options from the Tools menu.
    2. On the Security tab of the Internet Options dialog box, select the Local Intranet Web Content Zone and then click Custom Level.
    3. Locate the Miscellaneous/Access Data Sources Across Domains setting, and then select Enable, as shown in Figure 2.
    4. Click OK, and then click Yes to the warning dialog box that appears.
    5. Click OK to close the Internet Options dialog box.


    Figure 2. Enabling cross-domain data access in Internet Explorer.

    These Internet Explorer security options are per machine settings. If you choose to use this solution as part of deployment, you will need to follow the above steps on each machine that will be using your form.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Hide a Check Box Using Conditional Visibility

    Check Box and Option Button controls have very limited conditional formatting choices. In fact, the InfoPath UI provides only one conditional formatting choice for these two controls: disable. But disabling a control is not always the right choice. In certain conditions you might prefer to hide the control. Although the techniques described here will work with either control, we will focus on the check box.

    In this task we will create a simple form that a salvage yard could use to list the features of vehicles it has for sale. We will create a repeating table of vehicles which will include a drop-down list of vehicle types (car, motorcycle, or bicycle), and a set of five check boxes to represent the vehicle features (tires, engine, seat, steering wheel, and handlebars). Since no vehicle type requires the full set of features, we will hide certain check boxes based on the selected vehicle type.

    Two possible layouts for your check boxes are vertical and horizontal. Each layout will examine a different approach to hiding a check box using conditional visibility. For a vertical layout we will use a containing section. For a horizontal layout we will manually modify the XSL. Let's start by designing a new blank form.

    Create the schema:

    1. Open the Data Source task pane.
    2. Double-click myFields, and then rename it to Vehicles.
    3. Add a repeating Group named Vehicle.
    4. Select the Vehicle group, and then add five True/False Attribute Fields named tires, engine, seat, steeringWheel, and handlebars, with their default values set to False.
    5. Add three Text Fields named Id, Type, and Description.


    APPROACH 1 – VERTICAL LAYOUT: USE A CONTAINING SECTION

    With a vertical layout, the easiest approach to hide a check box using conditional visibility is to place it into a section, and then add the conditional visibility to the section. The advantage of this approach is that you can accomplish your task from within the designer.

    A section is rendered as a block element, meaning that you cannot place other controls next to it on the same line—they will be forced to appear either above or below the section. This is why this approach works well for a vertical layout, but not a horizontal layout. You can still use this approach with a horizontal layout by using a layout table, but a major drawback is that the empty space for the hidden check box will not be collapsed.

    Design the layout:

    1. In the Data Source task pane, right-click Vehicle, and then choose Repeating Section.
    2. Delete the extra blank lines from the repeating section, and then type VEHICLE:.
    3. Insert a 2x1 layout table.
    4. Right-click in the top table cell, and then choose Split Cells.
    5. In the Split Cells dialog box, type 2 for the number of columns, and then click OK.

    Insert the non-check box controls:

    1. From the Data Source task pane, drag-and-drop Id into the top-left table cell.
    2. Right-drag-and-drop Type into the top-right table cell, and then choose Drop-Down List Box.
    3. Drag-and-drop Description into the bottom table cell.

    Insert a section for each check box:

    1. Click on the first blank line below the layout table to set the insertion point.
    2. Right-click each of the five Boolean attribute fields (tires, engine, seat, steeringWheel, and handlebars), choose More | Section, and then click OK.

    Adjust the properties of the sections:

    1. Ctrl-Click the five sections you just inserted to create a multiple-selection, and then press Alt+Enter.
    2. In the Properties (Multiple Selection) dialog box, set each of the padding and margin values, except the right margin, to 0px, and then click OK.
    3. While you still have the multiple-selection, right-click one of the sections, and then choose Borders and Shading.
    4. On the Borders tab of the Borders And Shading dialog box, click None to remove the existing border, and then click OK.

    Insert the check box controls:

    1. Drag-and-drop each of the five Boolean attribute fields (tires, engine, seat, steeringWheel, and handlebars) into their associated section.
    2. Delete the extra blank lines inside each of the five sections. Your completed layout should look like Figure 1.

    Figure 1. The completed vertical layout of conditionally visible check boxes.

    Add entries to the Type drop-down list box:

    1. Double-click the Type drop-down list box, and then click Add.
    2. In the Add Choice dialog box, type Car, and then click OK.
    3. Add two more entries for Motorcycle and Bicycle.

    Add a rule to set the engine attribute value to false as its check box become hidden:

    1. Click Rules, and then click Add.
    2. Name the rule Set engine = false, and then click Set Condition.
    3. In the Condition dialog box, select Type Text from the third drop-down list, type Bicycle, and then click OK.
    4. Click Add Action.
    5. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    6. Click the Select XPath button to the right of the Field text box, select Vehicles/Vehicle/engine, and then click OK.
    7. Click the Insert Formula button to the right of the Value text box, click Insert Function, select false() from the list Functions, and then click OK four times.

    Add a rule to set the steeringWheel attribute value to false as its check box become hidden:

    1. Click Add.
    2. Name the rule Set steeringWheel = false, and then click Set Condition.
    3. In the Condition dialog box, select Type Text from the third drop-down list, type Motorcycle, click And, and then select Or in place of And.
    4. Select Type Text from the third drop-down list, Bicycle (as shown in Figure 2), and then click OK.

    Figure 2. The rule conditions for setting the steeringWheel attribute to false.

    1. Click Add Action.
    2. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    3. Click the Select XPath button to the right of the Field text box, select Vehicles/Vehicle/steeringWheel, and then click OK.
    4. Click the Insert Formula button to the right of the Value text box, click Insert Function, select false() from the list Functions, and then click OK four times.

    Add a rule to set the handlebars attribute value to false as its check box become hidden:

    1. Click Add.
    2. Name the rule Set handlebars = false, and then click Set Condition.
    3. In the Condition dialog box, select Type Text in the third drop-down list, and then type Car, and then click OK.
    4. Click Add Action.
    5. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    6. Click the Select XPath button to the right of the Field text box, select Vehicles/Vehicle/handlebars, and then click OK.
    7. Click the Insert Formula button to the right of the Value text box, click Insert Function, select false() from the list Functions, and then click OK six times to close all open dialog boxes.

    Add conditional visibility to the Engine section:

    1. Double-click the Engine section.
    2. On the Display tab of the Section Properties dialog box, click Conditional Formatting, and then click Add.
    3. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, and then type Bicycle.
    4. Select the Hide This Control check box, and then click OK three times.

    Add conditional visibility to the Steering Wheel section:

    1. Double-click the Steering Wheel section.
    2. One the Display tab of the Section Properties dialog box, click Conditional Formatting, and then click Add.
    3. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, type Motorcycle, click And, and then select Or in place of And.
    4. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, and then type Bicycle.
    5. Select the Hide This Control check box (as shown in Figure 3), and then click OK three times.

    Figure 3. Visiblity conditions for the Steering Wheel section.

    Add conditional visibility to the Handlebars section:

    1. Double-click the Handlebars section.
    2. One the Display tab of the Section Properties dialog box, click Conditional Formatting, and then click Add.
    3. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, and then type Car.
    4. Select the Hide This Control check box, and then click OK three times.

    Try it:

    1. Preview the form.
    2. Select each option from the Type drop-down list to see how the check boxes and label get hidden as appropriate.

    You now have a dynamic vertical list of check boxes that are hidden, as appropriate, based on the vehicle type. The check boxes that are hidden are automatically cleared so that there are no invalid selections when the form is saved.


    APPROACH 2 – HORIZONTAL LAYOUT: MANUALLY MODIFY THE XSL

    With a horizontal layout, the easiest approach to hide a check box using conditional visibility is to use a conditionally visible expression box for the label, and then manually modify the XSL to add conditional visibility to the check box control. You can use this approach with a vertical layout as well, but since it requires going outside of the designer to accomplish this we recommend that you instead use the first approach for a vertical layout.

    Design the layout:

    1. In the Data Source task pane, right-click Vehicle, and then choose Repeating Section.
    2. Delete the extra blank lines from the repeating section, and then type VEHICLE:.
    3. Insert a 2x1 layout table.
    4. Right-click in the top table cell, and then choose Split Cells.
    5. In the Split Cells dialog box, type 2 for the number of columns, and then click OK.

    Insert the controls:

    1. From the Data Source task pane, drag-and-drop Id into the top-left table cell.
    2. Right-drag-and-drop Type into the top-right table cell, and then choose Drop-Down List Box.
    3. Drag-and-drop Description into the bottom table cell.
    4. Drag-and-drop each of the five Boolean attribute fields (tires, engine, seat, steeringWheel, and handlebars) below the bottom table cell.

    Add entries to the Type drop-down list box:

    1. Double-click the Type drop-down list box, and then click Add.
    2. In the Add Choice dialog box, type Car, and then click OK.
    3. Add two more entries for Motorcycle and Bicycle.

    Add a rule to set the engine attribute value to false as its check box become hidden:

    1. Click Rules, and then click Add.
    2. Name the rule Set engine = false, and then click Set Condition.
    3. In the Condition dialog box, select Type Text from the third drop-down list, type Bicycle, and then click OK.
    4. Click Add Action.
    5. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    6. Click the Select XPath button to the right of the Field text box, select Vehicles/Vehicle/engine, and then click OK.
    7. Click the Insert Formula button to the right of the Value text box, click Insert Function, select false() from the list Functions, and then click OK four times.

    Add a rule to set the steeringWheel attribute value to false as its check box become hidden:

    1. Click Add.
    2. Name the rule Set steeringWheel = false, and then click Set Condition.
    3. In the Condition dialog box, select Type Text from the third drop-down list, type Motorcycle, click And, and then select Or in place of And.
    4. Select Type Text from the third drop-down list, Bicycle, and then click OK.
    5. Click Add Action.
    6. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    7. Click the Select XPath button to the right of the Field text box, select Vehicles/Vehicle/steeringWheel, and then click OK.
    8. Click the Insert Formula button to the right of the Value text box, click Insert Function, select false() from the list Functions, and then click OK four times.

    Add a rule to set the handlebars attribute value to false as its check box become hidden:

    1. Click Add.
    2. Name the rule Set handlebars = false, and then click Set Condition.
    3. In the Condition dialog box, select Type Text in the third drop-down list, and then type Car, and then click OK.
    4. Click Add Action.
    5. In the Action dialog box, select Set A Field's Value from the Action drop-down list.
    6. Click the Select XPath button to the right of the Field text box, select Vehicles/Vehicle/handlebars, and then click OK.
    7. Click the Insert Formula button to the right of the Value text box, click Insert Function, select false() from the list Functions, and then click OK six times to close all open dialog boxes. The complete set of rules is shown in Figure 4.

    Figure 4. The set of rules for the Type drop-down list box.

    Fix the layout of the check boxes:

    1. Remove the space character between each check box and its label.
    2. Remove any line breaks such that all of the check boxes are on the same line.
    3. Ctrl-Click the check boxes labeled Engine, Seat, Steering Wheel, and Handlebars to create a multiple-selection, and then press Alt+Enter.
    4. In the Properties (Multiple Selection) dialog box, set the left margin value to 10px, and then click OK.

    It is important to use margins, rather than space characters, to create gaps between the check boxes. If we use spaces, they will remain when the check box is hidden, causing the gaps to appear wider than they should. Since margins are part of the Check Box control, they are hidden along with the check box.

    Replace the Engine check box label with a conditionally visible expression box:

    1. Select the label text for the Engine check box.
    2. Open the Controls task pane, and then click Expression Box to insert one in place of the selected label text.
    3. In the Insert Expression Box dialog box, type "Engine", and then click OK.
    4. Double-click the new expression box.
    5. On the Size tab of the Expression Box Properties dialog box, set the width to auto, set each of the padding and margin values, except the right margin, to 0.
    6. On the Display tab, click Conditional Formatting, and then click Add.
    7. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, and then type Bicycle.
    8. Select the Hide This Control check box, and then click OK three times.

    Replace the Steering Wheel check box label with a conditionally visible expression box:

    1. Select the label text for the Steering Wheel check box.
    2. In the Controls task pane click Expression Box to insert one in place of the selected label text.
    3. In the Insert Expression Box dialog box, type "Steering Wheel", and then click OK.
    4. Double-click the new expression box.
    5. On the Size tab of the Expression Box Properties dialog box, set the width to auto, set each of the padding and margin values, except the right margin, to 0.
    6. On the Display tab, click Conditional Formatting, and then click Add.
    7. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, type Motorcycle, click And, and then select Or in place of And.
    8. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, and then type Bicycle.
    9. Select the Hide This Control check box, and then click OK three times.

    Replace the Handlebars check box label with a conditionally visible expression box:

    1. Select the label text for the Handlebars check box.
    2. In the Controls task pane click Expression Box to insert one in place of the selected label text.
    3. In the Insert Expression Box dialog box, type "Handlebars", and then click OK.
    4. Double-click the new expression box.
    5. On the Size tab of the Expression Box Properties dialog box, set the width to auto, set each of the padding and margin values, except the right margin, to 0.
    6. On the Display tab, click Conditional Formatting, and then click Add.
    7. Select Type from the first drop-down list, select Is Equal To from the second drop-down list, select Type Text from the third drop-down list, and then type Car.
    8. Select the Hide This Control check box, and then click OK three times. Your completed layout should look like Figure 5.


    Figure 5. The completed horizontal layout of conditionally visible check boxes.

    Modify the view .xsl file:

    1. Choose Extract Form Files from the File menu.
    2. Select a location to save your extracted form files to, and then click OK.
    3. Close InfoPath to release the lock it places on your form files.
    4. Using a text editor, open the view1.xsl file.

    Add code to conditionally hide the Engine check box:

    1. Search for the text my:engine to locate the input element that represents your Check Box control that you want to hide.
    2. Add the following code immediately below the input element:

    <xsl:attribute name="style">MARGIN-LEFT: 10px;<xsl:choose>
            <xsl:when test="my:Type = &quot;Bicycle&quot;">DISPLAY: none</xsl:when>
        </xsl:choose>
    </xsl:attribute>

    It is important that you copy the value of the style attribute from the input element, and then paste it between the xsl:attribute and xsl:choose elements. You must also make sure to add a semi-colon directly before the xsl:choose element. Although the code above uses MARGIN-LEFT: 10px; this must match whatever value was in the style attribute.

    1. Remove the style attribute from the input element.

    It should be noted here that, rather than needing to type all of this code, you can simply copy the code from the expression box label for this check box (since it already is set up to hide), and then paste it over the check box code you will be replacing. Then move the contents of the style attribute from the input element, replacing the contents of the xsl:attribute element. Finally remove the style attribute from the input element.

    Add code to conditionally hide the Steering Wheel check box:

    1. Search for the text my:steeringWheel to locate the input element that represents your Check Box control that you want to hide.
    2. Add the following code immediately below the input element:

    <xsl:attribute name="style">MARGIN-LEFT: 10px;<xsl:choose>
            <xsl:when test="my:Type = &quot;Motorcycle&quot; or my:Type = &quot;Bicycle&quot;">DISPLAY: none</xsl:when>
        </xsl:choose>
    </xsl:attribute>

    1. Remove the style attribute from the input element.

    Add code to conditionally hide the Handlebars check box:

    1. Search for the text my:handlebars to locate the input element that represents your Check Box control that you want to hide.
    2. Add the following code immediately below the input element:

    <xsl:attribute name="style">MARGIN-LEFT: 10px;<xsl:choose>
            <xsl:when test="my:Type = &quot;Car&quot;">DISPLAY: none</xsl:when>
        </xsl:choose>
    </xsl:attribute>

    1. Remove the style attribute from the input element.

    Try it:

    1. Save the view .xsl file, and then close the text editor.
    2. Reopen your form template by right-clicking the manifest.xsf file and choosing Design.
    3. Preview the form.
    4. Select each option from the Type drop-down list to see how the check boxes and label get hidden as appropriate.

    Having made these changes, you now have a dynamic horizontal list of check boxes that are hidden, as appropriate, based on the vehicle type. The check boxes that are hidden are automatically cleared so that there are no invalid selections when the form is saved. Your manual modifications will roundtrip in the designer, and will even survive any subsequent changes you might make in the Check Box Properties dialog box. There is a little extra work that must be done to accomplish this approach, but ultimately the results are well worth the effort.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Create Miniature Navigation Buttons with Centered Arrows

    When your design calls for navigation buttons that are smaller than the default size, your attempts to resize the buttons and font within the designer might prove frustrating at best. The arrows seem to never center correctly. Creating miniature navigation buttons in InfoPath can be done, but you must make a simple manual addition to the inline style of the buttons in the view .xsl file.

    In this task we will create a set of four miniature navigation buttons with the arrows 9, 3, 4, and :, which will represent buttons for the first, previous, next and last items in a list. Let's start by designing a new blank form.

    Create the four miniature navigation buttons:

    1. Open the Controls task pane.
    2. Insert a Button into the view.
    3. Select the button, and then set its font size and font to 8pt Webdings.
    4. Double-click the button.
    5. On the General tab of the Button Properties dialog box, type 9 for the label, and then type btnFirst for the ID, as shown in Figure 1.

    Figure 1. Setting the properties for the navigation buttons.

    For the Webdings font the text '9' is the 9 arrow, '3' is the 3 arrow, '4' is the 4 arrow, and ':' is the : arrow.

    1. On the Size tab, set both the height and width values to 16px, and then click OK.
    2. Make three copies of the button so that there are a total of four buttons in the view.
    3. Change the labels and IDs of the three new buttons to 3, btnPrevious; 4, btnNext; and :, btnLast respectively.

    Now all four types of navigation arrows are present in the view. As shown in Figure 2, the arrows in the 16x16 buttons are cut off. This will be fixed by manually editing the XSL.

    Figure 2. The arrows are cut off in the miniature navigation buttons.

    Modify the inline style for each of the miniature navigation buttons:

    1. Choose Extract Form Files from the File menu.
    2. Select a location to save your extracted form files to, and then click OK.
    3. Close InfoPath to release the lock it places on your form files.
    4. Using a text editor, open your view .xsl file.
    5. Search for the text btnFirst, and then locate the style attribute on the same input element.
    6. Add the following property and value to the end of the inline style:

    ; LINE-HEIGHT: 5px.

    1. Add the same property and value to the end of the inline style for each of the remaining three miniature navigation buttons, which can be found by searching for the text btnPrevious, btnNext, and btnLast respectively.
    2. Save the view .xsl file, and then close the text editor.
    3. Reopen your form template by right-clicking the manifest.xsf file and choosing Design.

    With the addition of the line-height property to the inline style of each of the miniature navigation buttons, the arrows are no longer cut off, but are properly centered within the buttons, as shown in Figure 3.

    Figure 3. The arrows are no longer cut off in the modified miniature navigation buttons.

    ©2005 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

  • Keep Track of List Selections

    At some point it might be necessary to keep track of order of the selections a user makes in a single list box or drop-down list box. You can accomplish this by writing your own code in an OnAfterChange event handler, or you can use rules.

    In this task we will create a list box and track the order of the user's selections in another field in the main data source. We will examine both approaches mentioned in the opening paragraph. Let's start by designing a new blank form.

    Create the schema:

    1. Open the Data Source task pane.
    2. Add a Text Field named SelectionsViaRules.
    3. Add a Text Field named SelectionsViaCode.
    4. Add a Text Field named Choice.

    Design the view:

    1. Right-click myFields, and then choose Controls.
    2. Add a carriage return before each text box to place them on a new line.
    3. Double-click the text box labeled Selections Via Rules.
    4. On the Display tab of the Text Box Properties dialog box, select Read-Only check box and the Wrap Text check box, and then choose Expand To Show All Text from the Scrolling drop-down list.
    5. On the Size tab, set the width value to 651 (as shown in Figure 1), and then click OK.


    Figure 1. Adjusting the text box properties.

    1. Make all of the same adjustments to the text box labeled Selections Via Code as you did to the previous text box.
    2. Right-click the text box labeled Choice, and then choose Change To | List Box.

    Populate the list box:

    1. Double-click the List Box.
    2. On the Data tab of the List Box Properties dialog box, add five entries with values for the letters A, B, C, D, and E (as shown in Figure 2), and then click OK.


    Figure 2. Adding the choices to the list box.

    1. Stretch the list box down to reveal all of the entries we just added.


    APPROACH 1 – USE RULES TO TRACK SELECTIONS

    The most convenient approach for the design we have chosen for our form is to use rules to keep track of selections in the list box. The rules will allow us to filter out selections that we don't want to keep track of, such as the default blank selection, and to determine whether a previous selection has already been made so we know whether to add a separator between the selections.

    Add a rule for the first selection:

    1. Double-click the list box.
    2. Click Rules, click Add, and then name the rule Set Selection.
    3. Click Set Condition.
    4. For the first condition, choose Choice, and Is Not Blank from the drop-down lists.
    5. Click And, and then ensure that And, and not Or, is selected in the drop-down list.
    6. For the second condition, choose SelectionsViaRules, and Is Blank from the drop-down lists (as shown in Figure 3), and then click OK.


    Figure 3. The conditions of the Set Selection rule.

    1. Click Add Action.
    2. Select Set A Field's Value in the Action drop-down list.
    3. Click the Select XPath button to the right of the Field text box, select SelectionsViaRules, and then click OK.
    4. Click the Insert Formula button to the right of the Value text box, type the dot character, and then click OK twice.

      The dot character is the XPath abbreviated syntax for the self::node() location. In this scenario it represents the Choice node.

    5. Select the Stop Processing Rules When This Rule Finishes check box, and then click OK.

    Add a rule for additional selections:

    1. Click Add, and then name the rule Add Selection.
    2. Click Set Condition.
    3. For the first condition, choose Choice, and Is Not Blank from the drop-down lists, and then click OK.
    4. Click Add Action.
    5. Select Set A Field's Value in the Action drop-down list.
    6. Click the Select XPath button to the right of the Field text box, select SelectionsViaRules, and then click OK.
    7. Click the Insert Formula button to the right of the Value text box, and then type the following formula (as shown in Figure 4):

    concat(../my:SelectionsViaRules, ", ", .)

    Figure 4. The formula to add an additional selection to the text box.

    1. Click OK five times.

    As this formula might look confusing, what it is doing is concatenating the existing value of the SelectionsViaRules text box with a comma and a space character, and then with the currently selected value of the Choice list box.


    APPROACH 2 – USE CODE TO TRACK SELECTIONS

    If we have chosen a form design that is not conducive to using rules or we need more complex processing capabilities, then we should use code to keep track of selections in the list box. In this task we will only duplicate in code the behavior of the rules from the previous approach.

    Add code behind selection changes:

    1. Double-click the list box.
    2. On the Data tab of the List Box Properties dialog box, click Data Validation.
    3. In the Data Validation dialog box, select OnAfterChange from the Events drop-down list, and then click Edit.
    4. In the Microsoft Script Editor, replace the contents of the OnAfterChange event handler with the following code:

    if(eventObj.IsUndoRedo || "Insert" != eventObj.Operation)
        return;

    var oChoice = XDocument.DOM.selectSingleNode("my:myFields/my:Choice");
    if("" == oChoice.text)
        return;

    var oSelectionsViaCode = XDocument.DOM.selectSingleNode("/my:myFields/my:SelectionsViaCode");
    if("" == oSelectionsViaCode.text)
        oSelectionsViaCode.text = oChoice.text;
    else
        oSelectionsViaCode.text += ", " + oChoice.text;

    1. Choose Save from the File menu, and then close the Microsoft Script Editor.

    Try it:

    With the rules and code in place, let's preview the form. Make the following selections in the list box: A, B, D, A, C, and then E. As shown in Figure 5, both the text boxes keep track of the selections you made in the order you made them. They also include a separator between each selection.


    Figure 5. The order of the list box selections is stored in text fields.

    The text box used to store the user selections does not need to be visible in the view. If you do want to present them to the user, you can use an expression box instead of a read-only text box. You can also use a secondary data source instead of the main data source to keep track of the user selections if you do not have a need to save them as part of the form.

    ©2004 Greg Collins. All rights reserved. Licensed to Autonomy Systems, LLC for display on InfoPathDev.com.

More Posts « Previous page - Next page »
Copyright © 2003-2019 Qdabra Software. All rights reserved.
View our Terms of Use.