July 2004 - Posts - Greg Collins
in

InfoPath Dev

This Blog

Syndication

Greg Collins

July 2004 - Posts

  • Using Rules to Update Fields in a Table Row

    While working today on my example form, Populating Table Row After Selection (Rules), I had an epiphany—a possible way of accomplishing this without resorting to manually editing the XSL or using code.

    The concept was to create a Support secondary data source with a RowPosition node who's soul purpose was to store the current row.

    In the table row existed a drop-down list box and a series of other fields. When the selection changed in the drop-down list box, the other fields should be updated with the correct values from a SQL database. Here's what I did:

    When the selection changed, I fired a series of rules to populate the table row fields with the corresponding SQL database fields, filtered on the selection made in the drop-down list box. This works great for a single table row, but not so good for multiple rows.

    By adding an initial rule which sets the value of the RowPosition node in the Support DOM with the value of the XPath function count(../preceding-sibling::my:RowNode) + 1, I now have a reference to the current table row.

    At this point I updated each of the other rules to make use of the RowPosition node. Here's a before and after of one particular rule with the difference highlighted:

    BEFORE:

    xdXDocument:GetDOM("Customers")/dfs:myFields/dfs:dataFields/d:Customers/@CompanyName[../@ContactName = xdXDocument:get-DOM()/my:myFields/my:Customers/my:Customer/@my:Name]

    AFTER:

    xdXDocument:GetDOM("Customers")/dfs:myFields/dfs:dataFields/d:Customers/@CompanyName[../@ContactName = xdXDocument:get-DOM()/my:myFields/my:Customers/my:Customer[xdXDocument:GetDOM("Support")/Support/RowPosition]/@my:Name]

    With the addition of storing the current row and the change to the XPath for setting the field values, I was sure it would work. But, alas, it didn't. It didn't take long to figure out why. My initial guess was correct. The RowPosition was being filled in as a string. By making one more adjustment, and forcing it to be a number, the solution worked beautifully! Here's the final working version:

    FINAL:

    xdXDocument:GetDOM("Customers")/dfs:myFields/dfs:dataFields/d:Customers/@CompanyName[../@ContactName = xdXDocument:get-DOM()/my:myFields/my:Customers/my:Customer[number(xdXDocument:GetDOM("Support")/Support/RowPosition)]/@my:Name]

    There are other issue to tackle too, such as having a drop-down list box in each row of a table can quickly become a huge performance hit. If you are using a Master-Detail table, you can move the drop-down list box into the Detail table. This will certainly help as you will then only have a single source for the list instead of one per row.

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

  • Faulty Condition Expressions

    2004-07-03

    I uncovered this bug today while trying to figure out why an InfoPathDev example form I've been preparing hasn't been working correctly. I've reported it to my Microsoft contacts, but it is unlikely that there will be any fix for this in SP1 before it ships. This bug has caused me grief for a long time, and I finally figured out why.

    I have a Rule on a control with a condition that I built using the InfoPath UI formula builder.

    The expression that InfoPath created for me was this:

    msxsl:string-compare(., count(../my:Items/my:Item) - xdMath:Nz(../my:NumberOfRows) + 1) > 0

    But the problem is that InfoPath is forcing the condition to be validated as a string compare. Because of this false positives and negatives are returned in certain circumstances. For example, the string comparison of "4" > "10" will return TRUE.

    The expression I was trying to get was the following:

    . > (count(../my:Items/my:Item) - xdMath:Nz(../my:NumberOfRows) + 1)

    With the hand-made adjustment to the expression I am now comparing actual values instead of strings. This gives me the correct result of FALSE for a value comparison of 4 > 10.

    By manually updating the rule condition, the change will stay intact until you open the rule condition using the InfoPath UI again. Then it reverts back to using msxml:string-compare(). If you click cancel, your manual update is retained, but if you click OK you will have to fix it again.

    I consider this a data loss situation, because A) InfoPath did not give me the intended results when I first created the condition, and B) any time I make a change to the condition, it reverts back.

    The problems are that A) I did not know up-front that InfoPath was creating a string comparison, B) it took a lot of time to debug the problem, C) when the condition is modified, I’ll likely not remember that I need to manually modify the expression until I find that the form isn't function properly, and D) whenever someone else makes a change, they may never figure it out!

    If I post an example that uses this technique, and others were playing around in the rules to see what I did, A) they would never see the correct condition I manually created because InfoPath will automatically convert the expression back to using msxml:string-compare(), and thus they'll never be able to figure out how I made it work, B) after they look at the condition, the form may no longer function as intended, and C) unless I document this well, they'll never be able to create the same condition expression that I did.



    2004-07-09

    The InfoPath team responded to me today. Their email sparked an idea for a possible workaround, which ultimately did work. I'll discuss that solution after reviewing their comments and the two recommendations they gave, both of which work. I'll explain why one of their recommendations is good in some circumstances, and not in others. I'll also explain why their other recommendation will always work, as will mine.
     
    A member of the InfoPath team took a stab in the dark and made the assumption that my current node (represented by the dot in the expression) was a string datatype. He was correct. Because of this InfoPath defaulted to a string-based comparison. This was why it kept adding the msxsl:string-compare() function.
     
    The first recommendation that the InfoPath team gave me was to changing the string datatype into a number datatype. For my example I was creating, this was the correct approach. Making this change, InfoPath recognized the Integer datatype and left out the string comparison. This change is great when you have control over the schema and it makes sense (as was my case) to use a number datatype for the field in question. There are many times when this will not be the case. So another fix is needed.
     
    The second recommendation that the InfoPath team gave me was to create my own comparison function. You can do this by writing a function in code that in this case checks if value A is greater than value B. In my particular case I would write a function called IsGreater() which accepts either two numeric values or two nodes. Then in the rules I would call into this function using xdExtension like this:

    xdExtension:IsGreater(., count(../my:Items/my:Item) - xdMath:Nz(../my:NumberOfRows) + 1)

    The function should return a boolean (true/false).

    This solution is great because you can do whatever you want in code. You can get as complex as needed, with the entire XDocument DOM at your disposal. But many people do not want to write code, and some people don't know how to. This is also a bit more complex for the as simple a task as I was trying to do.

    So now on to my solution. The idea I had while reading their replies to me was that if I just add an XPath function number() around the current node (represented by the dot), that this should be enough to keep InfoPath from being able to "resolve" the XPath into something it can understand, thus forcing it to leave it alone. This too worked—and is much simpler than writing your own function, though less robust. Thus the resulting expression looked like this:

    number(.) > count(../my:Items/my:Item) - xdMath:Nz(../my:NumberOfRows) + 1)

    So there you have it—three solutions to a troubling problem. The biggest problem now is just recognizing that your condition is being modified. The only way to do that is to edit the condition and then change the first drop-down list box in the Condition dialog box to be "The expression". This allows you to see the full XPath expression to see if it is what you are expecting it to be. It is different, one of these workarounds should do the trick!

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

  • How to Really Depress a Button!

    Status: Not fixed as of the InfoPath 2003 SP2 release. 

    I uncovered this bug merely on 2004-07-03 and reported it to my Microsoft contacts.

    I discovered that you can get a button into a depressed state and have it stuck there until you release it with another click. It's really not a hard thing to do. And what's worse is then when the button is placed into this state it will not perform its assigned functionality because the click was never released. It is not until the click is released that the rules and code behind the button are processed.

    Here are the steps:

    1. Start with a new blank form.
    2. Insert a button into the view.
    3. Preview the form.
    4. Left-click the button, but don’t release.
    5. Right-click the button, and then release.
    6. Now release the left-click.

    Results: The button stays in a depressed state.

    1. Left-click the button.

    Results: The button returns to normal state.

    1. Close the preview.
    2. Add a rule to the button to show a dialog box message.
    3. Preview the form.
    4. Perform steps 4-6 above.

    Results: The rule did not fire.

    1. Left-click the button.

    Results: The rule fires.

    1. Close the preview.

    You can also use a UI.Alert() in place of the rule and the effect is the same. If you use both a rule and an Alert, the affect is the same, only when the button is finally released, the rule fires and then the code, as expected.

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

  • Min/Max Error Message Limitation

    I wrote to my Microsoft contact a while back to request that a future version of InfoPath make the repeating sections smart enough to realize that they have hit the specified min or max limit and then hide the appropriate widget for insert or remove.

    But while trying to discover a way to work around that, I noticed that the error message that pops up when a min or max condition is hit happens before, and instead of, the OnBeforeChange() event handler. This is bad because there is no way for the form developer to show their own error message. Instead you are stuck with the default cryptic error message that InfoPath supplies, namely:

    On insert:

    InfoPath cannot insert the section or row because of an error.

    Details:
    The attempted fragment operation cannot be completed because there is no valid insertion point for the fragment.

    On remove:

    The item cannot be removed.

    Details:
    Content for element '{http://schemas.microsoft.com/office/infopath/2003/myXSD/2004-07-01T23:47:14}group1' is incomplete according to the DTD/Schema.

    Another idea I had while playing with this, and recommended Microsoft implement in conjunction with the aforementioned idea is: Allow rules to separately control the insert and delete widgets and the menu items that appear.

    So for example, if you have a limit of 10 rows, but you only want the user to insert 3 a maimum of 3 rows under a certain condition, then you could set a rule that would disable the "Insert" feature until the condition was no longer valid. The same thing would work for removing rows.

    These would be very powerful and very useful.

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

  • Too Lazy to Switch Views

    STATUS: Fixed in InfoPath 2003 SP1

    DESCRIPTION:

    While working on a project for Microsoft, Patrick Halstead and I were using some complex Open Rules and were having trouble getting things to work right. So, under the pressure of a deadline, we decided to convert them to code in the OnLoad event handler. It was during this process that we uncovered this bug on 2004-03-19 and reported it to our Microsoft contacts. It was at that point unknown to Microsoft. After this our only solution was to convert back to using Open Rules, which, as complex as they were, we eventually resolved our issues and get them to work as desired.

    This bug is managed code specific, meaning it will not happen when using script. It is manifest when attempting to switch views in the OnLoad event handler, but only after the form has been cached locally.

    The first time you run the form, when it has not yet been cached, the view will switch as expected, but there-after, until you clear the form from the InfoPath form cache, the view will fail to switch.

    If you want see this happen, try this little experiment:

    1. Start Visual Studio.
    2. Create a new C# InfoPath Project, and name it ViewSwitchTest.
    3. In view 1, type the text View 1.
    4. Add a new view.
    5. In view 2, type the text View 2.
    6. In the Tools menu, select Programming | On Load Event.
    7. Enter the following line in the OnLoad event handler:

      thisXDocument.ViewInfos[1].IsDefault = true;


    8. Publish the form.
    9. Close InfoPath and Visual Studio.
    10. Open the form from the published location. It should open to view 2.
    11. Close InfoPath.
    12. Open the form again from the published location.

    BUG: The form opens to view 1.

    1. Clear the form out of the InfoPath cache.
    2. Open the form from the published location. It should open to view 2.
    3. Close InfoPath.
    4. Open the form again from the published location.

    BUG: The form opens to view 1.

    This can be a very bad bug if the need arises for it. So what can be done? The workaround is to use Open Rules to do your view switching. If you have complicated code to figure out which view to switch to, this may not be a viable alternative. But fortunately, most view switching during the OnLoad stage will be able to be accomplished in the Open Rules.

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

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