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.