Wednesday, February 25, 2015

For Each...EndFor GOTCHA!!!!

Working on a new concept for my Fake Tabs need, I decided to create a container class that will act as a page tab.  Then to emulate a pageframe, I control the position of the tab on each instances of the class.  Then pile those up on top of each other.  So if I need say 5 tabs, then that will be 5 of that container class that is piled on top of each other.  Switching tabs is done via switching Zorder().

With this fake tab, I can change colors per tab or use the same color but with different shades.  With the tab width auto-adjusting based on the caption I put into it plus its Font Name and Size, I am happy with the result.

While those can be achieved as well on the native VFP tab via turning Themes=.F. and TabStyle = 1 (Non-justified) properties of a pageframe object, I like to have a uniquely looking tab for my use and not that Windows 98 looking tabs when I need a colored tab; so the extra work on my end to achieve it.

Everything works to my satisfaction until I decided to add a sort of highlighter in the form of a dotted transparent shape to act as an indicator of the active tab.



While testing that highlighter though, I noticed that it sometimes does not seem to follow my instruction on the FOR EACH....ENDFOR  for there are times (not always) that two dotted shapes remain.  For you to help understand this, here are my codes:

For Each loObject In This.Parent.Objects FoxObject
      lnAbsLeft = m.lnLeft + loObject.Left + loObject._nTabLeft
      If Between(laObj(3),m.lnAbsLeft , m.lnAbsLeft + loObject._nTabWidth)
            loObject.ZOrder(0)
            loObject.shpActiveTab.Visible = .T.
      Else
            loObject.shpActiveTab.Visible = .F.
      Endif
Next

And a variation:

For Each loObject In This.Parent.Objects FoxObject
      loObject.shpActiveTab.Visible = .F.
      lnAbsLeft = m.lnLeft + loObject.Left + loObject._nTabLeft
      If Between(laObj(3),m.lnAbsLeft , m.lnAbsLeft + loObject._nTabWidth)
            loObject.ZOrder(0)
            loObject.shpActiveTab.Visible = .T.
      Endif
Next

And we expect that that shpActive (dotted shape) will only show once, right?  On a case to case basis, it does not:


Gotcha

I realized only a while ago prior to this post what is causing this, and while this may not be a bug, it is not documented either on my VFP help (at least on my stock copy of it).

The gotcha is that FOR EACH...ENDFOR relies on the z-order of objects subject to the loop.  It starts reading from the bottom of the z-order going to top.  So by changing the z-order of the objects while in the loop, then that switch may cause it to shortcircuit where the FOR EACH reading "again" the one that may have already passed the previous loop and is then put on top or in front of the z-order;  and that the one that is not read yet that goes below the z-order may no longer be read.

So the solution to my problem is simply allowing the loop to complete before applying new Zorder():

For Each loObject In This.Parent.Objects FoxObject
      lnAbsLeft = m.lnLeft + loObject.Left + loObject._nTabLeft
      loObject.shpActiveTab.Visible = .F.
      If Between(laObj(3),m.lnAbsLeft , m.lnAbsLeft + loObject._nTabWidth)
            loActive = loObject
            loObject.shpActiveTab.Visible = .T.
      Endif
Next
*Change Zorder outside of the loop
loActive.ZOrder(0)

This post  is for your awareness to never change a Zorder() of an object inside a FOR EACH loop!


No comments:

Post a Comment