none
Disappearing paragraph RRS feed

  • Question

  • My question is actually near the bottom of this long post, but I wanted to provide some explanation and test code.  Please don't be intimidated!

     

    I needed a macro to shift XML lines two spaces to the right.  Basically, I wanted to select a series of lines, then execute the macro.  Originally, I used Word's own find & replace to replace "<" with "  <", but couldn't find a way to limit it to changing only the first "<" in a given line (paragraph).  Replace(string, "<", "  <", 1, 1) seemed easy enough, but the challenge came in limiting the execution to only those lines I had selected (rather than the entire document).  By the way, the modified text will be pasted elsewhere, so I can't just use indentation.

     

    Here's some sample XML I used for testing:

     

    <order id="12345" status="INCOMPLETE" order-date="2012-01-30">
    <link rel="self" href="https://site.com..."/>
    <link rel="checkout" href="https://site.com..."/>
    <list id="list123" lastmodified="2012-01-30 13:30:53.753">
    <items>
    <item sku="12345678" id="abc123" backordered="false">
    <quantity>2</quantity>
    <description>Product to be purchased</description>
    <unit-price currency="USD">59.99</unit-price>
    <cost currency="USD">119.98</cost>
    <shipping-cost currency="USD" fulfillment-id="ff678">9.99</shipping-cost>
    <tax-cost currency="USD">0.0</tax-cost>
    </item>
    </items>
    </order>
    

     

     I want to highlight (with mouse or keyboard) the lines between <items> and </items>, and apply the macro to shift them two spaces to the right.  I started with the following code:

     

    Sub QueryResultShiftRight()
    Dim mypara As Paragraph
    Dim txt As String
      For Each mypara In Selection.Range
        txt = Replace(mypara.Range.Text, "<", "  <", 1, 1)
        mypara.Range.Text = txt
      Next mypara
    End Sub
    

     

     The problem was that it went into an infinite loop, continually shifting the first line to the right.  I thought that the problem might be the changing "Selection", although "For Each" should have built the list of paragraphs once.  Anyway, I decided to set a range to the initial selection, so that it couldn't change.  Here's my second pass.  The commented lines were for debugging, so that I could identify the paragraphs and original range.  Try it (stepping through) - it's fun!

     

    Sub QueryResultShiftRight()
    Dim mypara As Paragraph
    Dim txt As String
      Set rngOrg = Selection.Range
      For Each mypara In rngOrg.Paragraphs
    '    mypara.Shading.BackgroundPatternColor = wdColorBrightGreen
    '    rngOrg.Bold = True
        txt = Replace(mypara.Range.Text, "<", "  <", 1, 1)
        mypara.Range.Text = txt
    '    rngOrg.Bold = False
    '    mypara.Shading.BackgroundPatternColor = wdColorYellow
      Next mypara
    End Sub
    

     

    Well, that didn't work, either, but the following did.  Sort of.

     

    Sub QueryResultShiftRight()
    Dim rngOrg As Range
    Dim ix As Integer
      Set rngOrg = Selection.Range
      For ix = 1 To rngOrg.Paragraphs.Count
        rngOrg.Paragraphs(ix).Range.Text = Replace(rngOrg.Paragraphs(ix).Range.Text, "<", "  <", 1, 1)
      Next
      rngOrg.MoveEnd wdParagraph, 1 '--- see below
      rngOrg.Select
    End Sub
    

    The problem was with the last line.  It reselected the original range, except for the last originally selected line.  That's why I have the "MoveEnd" line above it.  Say I originally select 8 lines.  I step through the code and stop after "Set rngOrg...".  In the Immediate window, "?rngOrg.Paragraphs.Count" returns "8".  Fine.  I run the rest of the program and stop at "rngOrg.MoveEnd...".  Now, "?rngOrg.Paragraphs.Count" returns "7"!

     

    OK, here's my question (finally!).  It seems that changing a paragraph's text (actually paragraph.range.text) removes that paragraph from the range it was originally in.  Why?
    • Edited by bobbiker Friday, February 3, 2012 3:35 PM
    Friday, February 3, 2012 3:34 PM

Answers

  • In first & 2nd iteration no problem will occur.Beacuse you are working within range boundary (11th and 42nd char)

    In third iteration,as soon you are setting 35-44 to empty your range is redefined to 11-34.Because 44<sup>th</sup> char was defining the range and it’s deletion will shrink the range to last chr now available.(Char 44)

     If you agin put text here it will not expand the range because now it is beyond the limit of range.That is why I avoided the last char which is 13.

    Pls try with my code and confirm.

    • Marked as answer by bobbiker Monday, February 13, 2012 9:12 PM
    Saturday, February 11, 2012 6:10 AM
    Answerer

All replies

  • Try something simpler like this.

    Sub QueryResultShiftRight()
        Selection.Paragraphs.Indent
    End Sub
    



    Sid (A good exercise for the Heart is to bend down and help another up) Please do not email me your questions. I do not answer questions by email unless I get paid for it :) If you want, create a thread in VB.Net/Excel forum and email me the link and I will help you if I can.
    Sunday, February 5, 2012 4:52 PM
    Moderator
  • Thanks, Siddharth, but as I mentioned, I will be pasting the text elsewhere, so need physical spaces, not indentation.

    The simple formatting is not the real question here.  I'm wondering why changing a paragraph's text (paragraph.range.text) resets a pre-defined range, or sets the active range to something other than the paragraph being operated on.

    Monday, February 6, 2012 2:59 PM
  • Try the below code:Select the entire text and run.

    Sub QueryResultShiftRight()
        Dim rngOrg As Range
        Dim ix As Integer
        Set rngOrg = Selection.Range
        For ix = 1 To rngOrg.Paragraphs.Count
            rngOrg.Paragraphs(ix).Range.Text = "  " _
            & rngOrg.Paragraphs(ix).Range.Text
        Next
        rngOrg.MoveEnd wdParagraph, 1 '--- see below
        rngOrg.Select
    End Sub 

    Tuesday, February 7, 2012 12:26 PM
    Answerer
  • Thanks for the reply, S.  I'm not sure what the difference is between your code and mine, except I do a "replace()" and you simply add two spaces.  They both work for this case, but for other situations, I have to do more than just add spaces.

    In any case, your code has the same odd behavior mine does - when the loop is done, you still have to extend the original range (rngOrg.MoveEnd...) to reselect the original range.  That's because a paragraph is "getting lost".

    Here's a revision of the code that steps through the loop, indicating the problem.  Test text and explanation follow:

    Sub aaaQueryResultShiftRightTest02()
     On Error GoTo ErrorCode
     Dim rngOrg As Range
     Dim ix As Integer
     Set rngOrg = Selection.Range
     For ix = 1 To rngOrg.Paragraphs.Count
       MsgBox "Paragraph " & ix & " BEFORE text change." & vbCrLf & "Click to make current paragraph green."
       rngOrg.Paragraphs(ix).Shading.BackgroundPatternColor = wdColorBrightGreen
       MsgBox "Paragraph " & ix & vbCrLf & "Click to indent paragraph.  There is no code here to change selection or color."
     rngOrg.Paragraphs(ix).Range.Text = "  " & rngOrg.Paragraphs(ix).Range.Text
       MsgBox "Paragraph " & ix & " AFTER text change." & vbCrLf & "Click to make current paragraph yellow."
       rngOrg.Paragraphs(ix).Shading.BackgroundPatternColor = wdColorYellow
     Next
     rngOrg.MoveEnd wdParagraph, 1
     rngOrg.Select
     Exit Sub
    ErrorCode:
     MsgBox Err.Number & " - " & Err.Description & vbCrLf & _
       "Paragraph (current) " & ix & vbCrLf & _
       "rngOrg.Paragraphs.Count: " & rngOrg.Paragraphs.Count, vbOKOnly, "ERROR"
    End Sub

    Use this to test:

    <order id="ABC-123" status="INCOMPLETE" order-date="2012-01-30">
    <cart>
    <items>
    <item sku="12345678" id="ab1234567890" backordered="false">
    <quantity>2</quantity>
    <description>Product Name for Platform</description>
    <unit-price currency="USD">59.99</unit-price>
    </item>
    </items>
    </cart>
    </order>

    Select the three item lines (<quantity>, <unit-price>, <description>), and run the macro.

    Obviously, you have selected three paragraphs.  Therefore, rngOrg.Paragraphs.Count should be 3, right?  Thus, the loop should run three times.  If all you do is "indent" the text, as above, the loop is fine.  When you come out of the loop, however, rngOrg.Paragraphs.Count is 2!  The macro shows this when it tries to change the altered paragraph's color; the third loop throws an error, indicating that paragraph 3 doesn't exist in the collection.

    rngOrg is set at the beginning of the macro.  Why does changing paragraph text change the original range?  Why does changing paragraph text remove the paragraph from any collection?

    Tuesday, February 7, 2012 3:18 PM
  • if we use

    rngOrg.Paragraphs(ix).Range.InsertBefore Text:="  "

    instead of

    rngOrg.Paragraphs(ix).Range.Text = "  " & rngOrg.Paragraphs(ix).Range.Text

    that works ok.

    But as you mentioned you need to do something more we should try to solve the deliacte issue you mentioned.I shall experiment on it.

    Wednesday, February 8, 2012 8:04 AM
    Answerer
  • Thanks for your note.  In case it helps, here are some additional information about what I'm trying to do:

    Select a set of lines and, if the first non-blank character of each paragraph in the selection in "<", then add two spaces to the beginning of the paragraph.  This is the example I've mentioned above.  I later need to copy and paste those lines into text documents, so need to do indentation with spaces, rather than with paragraph or page formatting.  Yes, for this case I could insert "  ", but that wouldn't work with the other scenarios.

    In some cases, I need to shift only the lines beginning with an XML tag; I don't want to shift data lines.  I would add logic to determine if the line should be shifted.  Your recommendation of inserting two spaces would work here, as well.

    In a separate macro, do the same, but shift the lines left two spaces.  If there are not two spaces before the first "<", then do not shift the paragraph.  Here, I can't insert negative spaces.

    For selected lines, change the first occurrance of one string to another.  For example, say I have several of the following lines:

    <query urlQuery="http://www.company.com/staging/query?parms" urlHelp="http://www.company.com/staging/helpDoc.html">

    For the lines I select, I want to change the first URL to "...com/production/query...", but leave the second URL pointing to /staging/.  The text for "query?parms" varies, so I can't easily do a more complex replace.  As I mentioned up top, I'm not aware of any Word Find & Replace functionality that allows me to change only the first occurrence of a string in a paragraph, but it's very easy to do with VBA's replace() function.

    Basically, I have the need to select certain lines from a document, and then walk through each paragraph in that selection, changing the text in some way.  It's the text change that's causing the problems.

    Thanks, again for your help!

    Thursday, February 9, 2012 7:39 PM
  •  

    If you set a range in word and change in the range in your program word will adjust the change in range.

    In word every range is identified with absolute character position in document. Suppose you set a range with 2<sup>nd</sup> character to 8<sup>th</sup> character. And then delete 2 character inside the range in program.The range will be from 2<sup>nd</sup> to 6<sup>th</sup> char. Similarly if you insert within range 2 character then the range will refer to 2<sup>nd</sup> to 10<sup>th</sup> character.

    In this case, when you are replacing and setting new text word first reset the range text to empty then assigns new text hence the range is redefined.

    Sub QueryResultSsdhiftRight()
        Dim rngOrg As Range
        Dim ix As Integer
        Set rngOrg = Selection.Range
       
        For ix = 1 To rngOrg.Paragraphs.Count
            With rngOrg.Paragraphs(ix).Range
                ActiveDocument.Range(.Start, .End - 1).Text = "  " _
                    & ActiveDocument.Range(.Start, .End - 1).Text
            End With
        Next ix
       
        rngOrg.Select
    End Sub

    Friday, February 10, 2012 9:49 AM
    Answerer
  • OK, so let's say I had the following text.  With the carriage return (char(13)) on each line, there are 10 characters per line:

    111111111
    123456789
    223456789
    323456789
    111111111

    I highlight the three middle lines, so Selection, and, thus, rngOrg goes from character 11 to character 40 ("11-40", for short).  The macro then executes the loop for the first time.

    Here, ix=1, so, initially, rngOrg.Paragraphs(1).Range goes from character 11 to character 20 (11-20).  The macro then changes the text:

    rngOrg.Paragraphs(1).Range.Text = "  " & rngOrg.Paragraphs(1).Range.Text

    If I follow what you're saying, Word first sets the Range.Text to empty.  Therefore, 10 characters have been eliminated  Therefore, rngOrg is now 11-30.  Word then enters 12 characters (two spaces and the original 10).  Now, rngOrg.Paragraphs(1).Range is 11-22.  That should also make rng.Org 11-42.

    OK, I'm now on the last iteration (ix=3).  After the text replacement, rngOrg.Paragraphs(3).Range should be 35-46.  rngOrg should be 11-46.  (Man, this would be easier with animation!)  Is this correct so far?  If not, what am I missing?

    If this is correct, then why are there now only two paragraphs in rng.Org?  I know that's true, because if I then refer to rngOrg.Paragraphs(3), I get the "member not found" error, and rngOrg.Paragraphs.Count=2.  That still leaves me wondering, why?


    • Edited by bobbiker Monday, February 13, 2012 3:21 PM
    Friday, February 10, 2012 4:57 PM
  • In first & 2nd iteration no problem will occur.Beacuse you are working within range boundary (11th and 42nd char)

    In third iteration,as soon you are setting 35-44 to empty your range is redefined to 11-34.Because 44<sup>th</sup> char was defining the range and it’s deletion will shrink the range to last chr now available.(Char 44)

     If you agin put text here it will not expand the range because now it is beyond the limit of range.That is why I avoided the last char which is 13.

    Pls try with my code and confirm.

    • Marked as answer by bobbiker Monday, February 13, 2012 9:12 PM
    Saturday, February 11, 2012 6:10 AM
    Answerer
  • Thanks, S.  I did, indeed, miss the fact that you were operating on a reduced version of the range (end-1).  Your code works!  It's a bit more convoluted than I would have expected to be necessary, but does make sense given your explanation.

    If there's a typo in your last post, then I think I've got it!  You say, "the range and it’s deletion will shrink the range to last chr now available.(Char 44)".  Do you mean "(Char 34)"?  If so, all's right.  If not, please let me know, once again, what I'm missing!

    Thanks so much for your explanations.

    Monday, February 13, 2012 3:20 PM
  • Yes.It is a typo.

    Pls mark this as answer if the post helps.You deserve lot of thanks from me for giving that nice scope.

    Monday, February 13, 2012 3:34 PM
    Answerer