Results 1 to 7 of 7
  1. #1
    5 Star Lounger st3333ve's Avatar
    Join Date
    May 2003
    Location
    Los Angeles, California, USA
    Posts
    705
    Thanks
    0
    Thanked 2 Times in 2 Posts

    More troubles with tables (Word 2002 SP-2)

    I'm getting consistent bugginess trying to search for the Normal style using a Range if (1) there's a table in the document, and (2) either there are no Normal paragraphs in the document or the only Normal paragraphs precede the table.

    To take a simple example: If I create a new document consisting of nothing but a handful of Body Text paragraphs and a table (with the table positioned several paragraphs down from the start of the document), and then I run the following code:
    <pre> Dim rngX As Word.Range
    Dim lngCount As Long

    Set rngX = ActiveDocument.Range(0, 0)
    With rngX.Find
    .Text = ""
    .Style = "Normal"
    .Format = True
    End With
    With rngX
    Do While .Find.Execute = True
    lngCount = lngCount + 1
    Debug.Print "Found at " & .Start & ", " & .End
    .Collapse wdCollapseEnd
    If lngCount = 3 Then
    Exit Do
    End If
    Loop
    End With

    Set rngX = Nothing
    </pre>

    the result in the Immediate Window is:
    <pre> Found at 0, 0
    Found at 0, 0
    Found at 0, 0
    </pre>

    Somehow .Find.Execute is returning True and yet rngX isn't moving forward to any found target. The code would loop endlessly if not for the "If lngCount = 3" exit line. And what's worse: If I set up the code so that it's replacing Normal with another style, Word tends to crash. (During one series of tests, it was pretty consistently crashing the 2nd time I ran the macro -- as if the first execution somehow put the document (or Word) in an unstable state, after which the 2nd execution delivered the knockout punch.)

    Some further experimentation involving the Selection object leads me to the conclusion that it's the end-of-row-markers in the table that are causing the problem. They're "Normal" style but they confuse Range.Find objects.

    Further note: If I change one of the paragraphs after the table to Normal style and then run the code, it works perfectly. (Likewise if I start the .Find with the Range already located after the table.)

    I realize one possible "solution" is to just make sure none of my documents ever have any "Normal" paragraphs (not counting end-of-row-markers), so I never have any reason to run a procedure that searches for the Normal style. Perhaps another "solution" would be to use the Selection object, although I don't really know that it's not also subject to some bugginess in this area.

    Is there a more direct solution -- i.e., a way to prevent the end-of-row markers from screwing up the Range.Find object?

  2. #2
    Plutonium Lounger
    Join Date
    Mar 2002
    Posts
    84,353
    Thanks
    0
    Thanked 29 Times in 29 Posts

    Re: More troubles with tables (Word 2002 SP-2)

    You could check whether the range actually moves:

    Sub Test()
    Dim rngX As Word.Range
    Dim lngCount As Long
    Dim lngStart As Long

    Set rngX = ActiveDocument.Range(0, 0)
    With rngX.Find
    .Text = ""
    .Style = wdStyleNormal
    .Format = True
    End With
    With rngX
    lngStart = .Start
    Do While .Find.Execute = True
    lngCount = lngCount + 1
    Debug.Print "Found at " & .Start & ", " & .End
    .Collapse wdCollapseEnd
    If .Start = lngStart Then
    Exit Sub
    Else
    lngStart = .Start
    End If
    Loop
    End With

    Set rngX = Nothing
    End Sub

    (I replaced "Normal" with wdStyleNormal - on my Dutch language system "Normal" has a different name). You'd still have to build in a check for the very first "find".

  3. #3
    5 Star Lounger st3333ve's Avatar
    Join Date
    May 2003
    Location
    Los Angeles, California, USA
    Posts
    705
    Thanks
    0
    Thanked 2 Times in 2 Posts

    Re: More troubles with tables (Word 2002 SP-2)

    Thanks for the suggestion. Here's an alternative that occurred to me that takes care of the initial "false positive" as well:

    <pre> Dim rngX As Word.Range
    Dim fSearching4Normal As Boolean

    Set rngX = ActiveDocument.Range(0, 0)
    With rngX.Find
    .Text = ""
    .Style = "Normal"
    .Format = True
    End With
    If rngX.Find.Style = "Normal" Then
    fSearching4Normal = True
    End If
    With rngX
    Do While .Find.Execute = True
    If fSearching4Normal = True Then
    If .Style <> "Normal" Then
    Exit Do
    End If
    End If
    'Do something here.
    .Collapse wdCollapseEnd
    Loop
    End With

    Set rngX = Nothing</pre>

    Another alternative would be to dispense with the .Find object entirely and iterate through the document's paragraphs, skipping end-of-row markers, like this:

    <pre> Dim parX As Word.Paragraph
    Dim rngTest As Word.Range

    For Each parX In ActiveDocument.Paragraphs
    With parX
    If .Style = "Normal" Then
    If .Range.Information(wdWithInTable) = True Then
    Set rngTest = .Range.Duplicate
    rngTest.Collapse wdCollapseStart
    If rngTest.Information(wdAtEndOfRowMarker) = True Then
    GoTo Skipped
    End If
    End If
    'Do something here.
    End If
    End With
    Skipped:
    Next parX

    Set parX = Nothing
    Set rngTest = Nothing</pre>

    Side question: I realize many professional programmers would scoff at my "GoTo Skipped" construction, but is there a non-clunky alternative? As I see it, the "clunky" alternatives would include (1) putting the "Do something here" code in 2 places, (2) putting the "Do somthing here" code in a separate procedure and calling it in 2 places, (3) combining the two .Information If tests in a single line (but that means a lot of wasted processing, since the rngTest stuff will then run even when you're not in a table -- and yes, I realize that, in this particular case, the first If test would then be redundant), and (4) adding a fDoIt boolean variable that's reset to True at the start of each loop, gets set to False in the EndOfRowMarker context, and controls whether the "Do something here" code runs. For some strange reason, I seem to prefer the GoTo approach, but maybe I'm missing an approach.

    EDITED TO ADD: I should also have mentioned the GoSub alternative, but my understanding is (1) that those that scoffs at GoTo also scoffs at GoSub, and (2) GoSub won't survive the move to .NET.

  4. #4
    3 Star Lounger
    Join Date
    Apr 2004
    Location
    Boston, Massachusetts, USA
    Posts
    389
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: More troubles with tables (Word 2002 SP-2)

    Hi Steve,

    You seem to be looking for input/suggestions, so here's my 2 cents:

    You're spot on with your critique of the Find object. It's great in some cases, but it's very difficult to corral in complex documents. Considering how long it can take to tweak the code when using Find in a complex test, I almost always turn to a For..Each loop first, and optimize that. 9 times out of 10, the performance is more than acceptable, without the risk of infinite loops. When it's still too slow after optimizing, that's when I'll take the time to put together code using Find. (I am of course generalizing here, some situations are best suited for Find, and the code can sometimes be quite simple.)

    I also think you've pretty much summed up the possibilities for handling this sort of condition. Many other languages have some sort of "Next" construct that allows you to skip to the next iteration in this kind of loop, which is perfect for this sort of situation. That, and variable interpolation are the things I miss most when using VBA.

    You said you didn't like one of the alternatives because it meant some wasted processing, and that's a very valid point. But I'd actually come to a different conclusion when looking at the options you described, relating to your code, specifically.

    First of all, evaluating a paragraph to see if it's actually the end of a table row sounds like a very useful thing to do, and would likely come up again in other macros. I'd break it out into a re-usable function.

    Second, the test you're using to deterimine if a paragraph is the end of a row is expensive. Duplicating and collapsing ranges repeatedly like that can really slow things down. I've suggested an alternate method, shown in the function IsParaEndOfRow shown below.

    Combining these two points, the following is the code I'd use in your situation. It's about the same number of total lines, but is also re-usable. It's also substantially faster. On a 500 page document, your macro took 2:38, and the one below, just 9 seconds.

    <pre>Sub DoStuffToNormalParas()
    Dim para As Paragraph
    For Each para In ActiveDocument.Paragraphs
    If para.Style = wdStyleNormal Then
    If Not IsParaEndOfRow(para) Then
    ' do stuff here
    End If
    End If
    Next para
    End Sub
    '
    Function IsParaEndOfRow(para As Paragraph) As Boolean
    If para.Range.Information(wdWithInTable) = True Then
    If para.Range.Cells.Count = 0 Then
    IsParaEndOfRow = True
    Exit Function
    End If
    End If
    IsParaEndOfRow = False
    End Function
    </pre>


    I haven't exhaustively tested this code, and I welcome any suggestions or feedback.

    Cheers!

  5. #5
    5 Star Lounger st3333ve's Avatar
    Join Date
    May 2003
    Location
    Los Angeles, California, USA
    Posts
    705
    Thanks
    0
    Thanked 2 Times in 2 Posts

    Re: More troubles with tables (Word 2002 SP-2)

    <img src=/S/cheers.gif border=0 alt=cheers width=30 height=16> Identifying an EndOfRowMarker by determining (1) that you're in a table and (2) the .Cells.Count is 0 is a great idea (especially since it's so much faster than the alternative), and I've tucked IsParaEndOfRow away in my macro library.

    Thanks for all your input. I'll definitely keep the object-iteration approach in mind as (at least) a backup any time a Find loop seems to be unavoidably misbehaving.

  6. #6
    Platinum Lounger
    Join Date
    Feb 2001
    Location
    Yilgarn region of Toronto, Ontario
    Posts
    5,453
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: More troubles with tables (Word 2002 SP-2)

    > your macro took 2:38, and the one below, just 9 seconds

    Hi Andrew. Is this what you meant? Is your macro significantly slower than Steve's?

    Steve led me here after reading my post.

  7. #7
    3 Star Lounger
    Join Date
    Apr 2004
    Location
    Boston, Massachusetts, USA
    Posts
    389
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Re: More troubles with tables (Word 2002 SP-2)

    No, mine is significantly faster than Steve's, as it uses a less expensive test for the end-of-row condition.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •