none
objRange.Information(wdWithinTable) oddity RRS feed

  • General discussion

  • Word 2010 SP1

    For what it's worth, this took me a while to track down; maybe will benefit others. It may be documented somewhere, but I sure didn't find it.

    Empirically, when the Selection is not within the Range against which Range.Information(wdWithinTable) is run, Word quietly moves the Selection to objRange, determines the return value for Selection.Information(wdWithinTable), and then returns the Selection to its prior location, hoping no one will notice. (Sorta like what we used to do with WordBasic when we didn't HAVE ranges!)

    But when that Selection change triggers unexpected Document Events, we do notice. Boy do we notice. Word apparently does not suppress the Events.

    If you don't care how I came to this conclusion, just remember not to use Range.Information against a Range that does not match Selection, unless doing so won't trigger any Events or do any other damage. Selection.Information is safe, AFAIK. Personally, I'll likely never use Range.Information again!

    I also do not know if it affects any other .Information(Type)'s. Didn't check.

    However, if you want to check my logic, here's how I found out Word's dirty little secret:

    I have a procedure that deletes a Range that includes a few ContentControls. Before the deletion, it needs to move the Selection to a ContentControl that will survive the deletion (in the example below, we'll make the .Tag "Survive"), in order to avoid another procedure choking when the user's currently-selected Content Control (in this case, "Doomed") is deleted. That all works as it's supposed to.

    In the example, "Doomed" is not in a Table.

    The takeaway from the above is that Selection.Range <> objRange.

    What I found was that the Document_ContentControlOnExit event was triggered against "Doomed", when the control was exited by my procedure. As expected. But it was triggered again after that. Definitely not expected. The exit event posts a dialog, so it's pretty obvious when it's running.

    The Exit Event did not run if I set breakpoints to find out where it was happening. So I made added these Debug.Print's to the procedure--

    [snip]
    Debug.Print 1
    If objRange.Information(wdWithInTable) Then
      Debug.Print 2
    [snip]
    
    

    --and added this to the top of the CCOnEnter event--

    Debug.Print "entering " & ContentControl.Tag
    

    --and this to the top of the Exit event--

    Debug.Print "exiting " & ContentControl.Tag

    --and then reproduced the problem. Here is the output from the Immediate Window, with my annotations in italics:

    entering Survive expected; triggered by my procedure

    1 last line before objRange.Information(wdWithinTable)

    exiting Doomed unexpected; apparently triggered by .Information

    entering Survive unexpected; apparently triggered by .Information

    3 another debug.print that follows .Information. '2' is missing because objRange isn't in a Table, so that clause of the If statement doesn't execute, which includes debug.print 2, is expected not to run

    So I'm about to replace the .Information statement with--

     

    For Each objTable in ActiveDocument.Tables
     If objRange.InRange(objTable) Then
      blnWithinTable = True
     End If
    Next objTable
    
    

     

    --and advise all to be careful about this gotcha when using Range.Information.


    Saturday, July 16, 2011 8:12 PM

All replies

  • <<Empirically, when the Selection is not within the Range against which Range.Information(wdWithinTable) is run, Word quietly moves the Selection to objRange, determines the return value for Selection.Information(wdWithinTable), and then returns the Selection to its prior location, hoping no one will notice. (Sorta like what we used to do with WordBasic when we didn't HAVE ranges!)>>

    Like, just, WOW! Great detective work!! And thank you so much for sharing this :-)

    Range.Information does, I'm sure, base on the old WordBasic (nice to run into someone who remembers those times :-)) I'd never thought it through, however, all the way to the fact that this would surreptitiouly change the SELECTION <shake of the head). Logical, but still not expected.

    A remark to your code change: Looping all the tables in the document is going to slow things down. This approach might be faster:

      If Range.Tables.Count>0 Then 'Or = 1

    Of course, if your expected Range would contain multiple characters and you need to trap whether it's actually IN (rather than "holding") a table, set a second Range to either Range.Start or Range.End and test that.

    (If a Range or selection is in a table, Tables.Count returns 1)


    Cindy Meister, VSTO/Word MVP
    Sunday, July 17, 2011 6:36 AM
    Moderator
  • Very interesting, and thank you for sharing.
     
    Although some Events are undoubtedly firing when they shouldn't, I think you are jumping to conclusions when you say that the Selection is actually being changed.
     
    From a few quick tests it seems to me that Word - after doing its stuff for the Range.Information - is (in some circumstances) assuming that the Selection has just changed, and is firing whatever events it deems appropriate, those for the previous change of selection which it has previously fired.
     

    Enjoy,
    Tony
    www.WordArticles.com
    Sunday, July 17, 2011 11:44 AM
  • Thanks for the tip, Cindy. I can see where Range.Tables.Count would be a lot better, not to mention less code. Will do it that way.

    Reverse engineering is never conclusive, Tony. Hence my deliberate use of the word "empirically". It's a theory, like yours, with mine better supported by Occam's Razor. But we can probably agree that whether Word is really changing the Selection, or (in some circumstances) merely appears to change the Selection, the net is the same: Events are firing as though the Selection had, in fact, changed, when the Selection should not be moving at all. Which--no "empirically" about it--is a very, very bad thing!

    Monday, July 18, 2011 3:10 PM
  • One thing to watch out for:

    If a range happens to start immediately following a table, and you collapse the range, Range.Tables.Count will be 1, not 0.

    A second thing to watch out for:

    Even if no event handlers exist, the Range.Information property can trigger an immediate pagination, and in long documents, that can be a real performance hit.

    --Bill


    Bill Coan
    Thursday, October 27, 2011 7:46 PM