![]() |
|
|
|
#1
|
|||
|
|||
|
Hi everyone,
maybe I am overlooking something very obvious... but, as the topic suggests I have a problem with paragraphs that appear to both exist and not to exist at the same time. The problem occurs always when when there is an empty paragraph at the end of a content control. I am looking for a way to target this paragraph efficiently and remove it. My problem is mainly that I cannot find a way to target these paragraphs directly for whatever reason! I've built a small example to demonstrate my problem: Lets say I have a content control cc in that one can select three paragraphs (screenshot) of which the last one is empty. The test document looks like this: ![]() It has 6 paragraphs in total of which 2 are placed before the content control, three are or lets say should be within the cc and one is/should be after the cc. The cc is a rich text cc, if that matters. When I run the code Code:
Sub countParasInCC()
Dim doc As Document, cc As ContentControl, ccRange As Range, paraCountCC As Integer, myccTitle As String
Set doc = ActiveDocument
myccTitle = "ccTitle"
Set cc = doc.SelectContentControlsByTitle(myccTitle).Item(1)
Set ccRange = cc.Range
paraCountCC = ccRange.Paragraphs.Count
End Sub
However, when i run the code Code:
Sub countParasDocument()
Dim doc As Document, paraCountDoc As Integer
Set doc = ActiveDocument
paraCountDoc = doc.Range.Paragraphs.Count
End Sub
Can somebody explain to me why the empty paragraph is not counted when counting paragraphs in the cc's range but is counted when counting paragraphs in the document range? What is the method to target and remove such paragraphs? Many thanks in advance! |
|
#2
|
|||
|
|||
|
It is just a empty paragraph that Microsoft has pressed a placeholder role on.
Select your empty paragraph in the CC and step through this code: Code:
Sub SSM()
Dim oCC As ContentControl
Dim oPar As Paragraph
Dim oRng As Range
Dim lngIndex As Long
Set oCC = ActiveDocument.SelectContentControlsByTitle("ccTitle").Item(1)
MsgBox oCC.Range.Paragraphs.Count
'Select the empty paragraph that appears to be in the CC. Is it really there?
MsgBox Selection.InRange(oCC.Range)
'No. Try to delete it.
Selection.Range.Delete
'You can't. Why?
'Show the tags. Notice the selection shift
oCC.Appearance = wdContentControlTags
'It is smoke and mirrors. The empty paragraph is the placeholder for the XML tags
For lngIndex = ActiveDocument.Paragraphs.Count To 1 Step -1
If Selection.Range.InRange(ActiveDocument.Paragraphs(lngIndex).Range) Then
ActiveDocument.Paragraphs(lngIndex).Range.Delete
End If
Next
lbl_Exit:
Exit Sub
End Sub
|
|
#3
|
|||
|
|||
|
Thanks Greg, that put me in the right direction. I thought that I can target the paragraph within the cc specifically but now I just iterate through the entire document.
My goal is to find all empty paragraphs in documents with 3000 (or sometimes more) paragraphs that do not contain necessary breaks and delete them. Tables are excluded by design. There is some redundancy in the code regarding the checking for the breaks which I was too lazy to remove. I am fairly new to VBA, so, if anyone has a suggestion for improvement, please share your thoughts. Code:
Sub delete_empty_paras()
Dim doc As Document, para As Paragraph, paraNext As Paragraph, paraRng As Range, _
paraLen As Integer, delCount As Integer, paraCount_pre As Integer, paraCount_post As Integer
StartTime = Timer
Set doc = ActiveDocument: Set para = doc.Paragraphs.First
Do While Not para Is Nothing
Set paraNext = para.Next
If para.Range.Tables.count > 0 Then GoTo Skip
Set paraRng = para.Range: paraLen = Len(para.Range.text)
If paraRng.text = ChrW(12) Then GoTo Skip:If paraRng.text = "^m" Then GoTo Skip
If paraLen <= 2 Then
If Not ContainsBreaks(paraRng) And Not ContainsShapesOrImages(paraRng) Then
paraCount_pre = doc.Paragraphs.count
'paraRng.Select 'debugging
paraRng.Delete: paraCount_post = doc.Paragraphs.count: del = IIf(paraCount_post <> paraCount_pre, 1, 0)
delCount = delCount + del
End If
End If
Skip:
Set para = paraNext
Loop
Debug.Print "Paragraphs deleted: " & delCount & " | Execution time: " & _
Round(Timer - StartTime, 2) & " s | " & Format((Timer - StartTime) / 60, "0.00") & " min"
End Sub
Code:
Function ContainsShapesOrImages(rng As Range) As Boolean
Dim iLshp As InlineShape, shp As shape
Dim found As Boolean
found = False
For Each iLshp In rng.InlineShapes 'check for InlineShapes
found = True: Exit For
Next iLshp
If Not found Then 'check for regular shapes. do I even need this?
For Each shp In rng.ShapeRange
found = True: Exit For
Next shp
End If
ContainsShapesOrImages = found
End Function
Code:
Function ContainsBreaks(rng As Range) As Boolean
Dim searchText As Variant, found As Boolean, i As Integer
searchText = Array("^b", "^m", "^12", "^n") 'checks for specific breaks
found = False
With rng.Find
.ClearFormatting: .Forward = True: .Wrap = wdFindStop: .MatchWildcards = False
For i = LBound(searchText) To UBound(searchText)
.text = searchText(i)
If .Execute Then: found = True: Exit For 'exit when found
Next i
End With
ContainsBreaks = found
End Function
Code:
Function StopTimer(StartTime As Double) As Double
Dim elapsedTime As Double
elapsedTime = Timer - StartTime
StopTimer = elapsedTime
End Function
|
|
#4
|
|||
|
|||
|
I think you are adding more bells and whistles than your really need. There is really no need to evaluate every paragraph. Perhaps something like:
Code:
Sub DeleteEmptyParagraphs()
Dim oRng As Range
Dim lngST As Long, lngCount As Long, lngIndex As Long
Dim oPar As Paragraph
lngST = Timer
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = Chr(13) & "{2,}"
.MatchWildcards = True
While .Execute
For lngIndex = oRng.Paragraphs.Count To 1 Step -1
Set oPar = oRng.Paragraphs(lngIndex)
If Len(oPar.Range.Text) = 1 Then
oPar.Range.Select
Select Case AscW(oPar.Range.Characters.Last.Next)
Case 12 'Do nothing
Case Else
oPar.Range.Delete
lngCount = lngCount + 1
End Select
End If
Next lngIndex
oRng.Collapse wdCollapseEnd
Wend
End With
MsgBox "Paragraphs deleted: " & lngCount & " | Execution time: " & _
Round(Timer - lngST, 2) & " s | " & Format((Timer - lngST) / 60, "0.00") & " min"
lbl_Exit:
Exit Sub
End Sub
|
|
#5
|
|||
|
|||
|
Thanks again Greg. I Like the idea but I cannot get it to work the way you intend.
I get error 5560 on line Code:
While .Execute Code:
.Text = Chr(13) & "{2,}"
Code:
Select Case AscW(oPar.Range.Characters.Last.Next) Besides that, why exactly do you use step -1 and why a 'for x=z to y' loop? Wouldn't it be much faster to to iterate manually when dealing with large documents? Like Code:
i = 1
For each oPar in ActiveDocument.Paragraphs
...<code>
Set oPar = ActiveDocument.Paragraphs(i)
...<code>
i = i +1
Next oPar
|
|
#6
|
|||
|
|||
|
genja,
Well obviously I failed to do exhaustive testing ![]() My point was to try to convey that there is little point in evaluating every paragraph against the next. For Example if Leo Tolstoy would have inadvertently left a couple of empty paragraphs in War and Peace, why evaluate the whole book. First find any instance where two or more paragraphs appear, then evaluate if the condition warrants removing them. I also failed it include the very issue you first posted about! Anyway. Let's try again. Again the testing is not exhaustive but here at least the code is doing better. As for your error on .Text = Chr(13) & "{2,} ... that may be due to your regional settings "," may not be your defined list separator. Code:
Sub DeleteEmptyParagraphs()
Dim oRng As Range, oRngEval As Range
Dim lngST As Long, lngCount As Long
Dim oPar As Paragraph
lngST = Timer
Set oRng = ActiveDocument.Range
With oRng.Find
.Text = Chr(13) & "{2,}"
.MatchWildcards = True
While .Execute
Set oRngEval = oRng.Duplicate
Set oPar = oRngEval.Paragraphs(1)
Do While Not oPar Is Nothing
oPar.Range.Select
Select Case True
Case Len(oPar.Range.Text) = 1
Select Case True
Case oPar.Range.End = ActiveDocument.Range.End
oPar.Range.Delete
lngCount = lngCount + 1
Case oPar.Range.Text = ChrW(12) Or oPar.Range.Characters.Last.Next = ChrW(12)
Set oPar = oPar.Next
Case Else
oPar.Range.Delete
lngCount = lngCount + 1
End Select
Case Else
Set oPar = oPar.Next
End Select
Loop
oRng.Collapse wdCollapseEnd
Wend
End With
MsgBox "Paragraphs deleted: " & lngCount & " | Execution time: " & _
Round(Timer - lngST, 2) & " s | " & Format((Timer - lngST) / 60, "0.00") & " min"
lbl_Exit:
Exit Sub
End Sub
|
|
| Tags |
| content control, delete empty, paragraphs |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
Remove extra space before a paragraph and return it to previous paragraph
|
laith93 | Word VBA | 7 | 04-27-2022 08:26 AM |
| a macro to replace paragraph mark with a space applies effect on paragraph marks after the selection | drrr | Word VBA | 2 | 08-24-2021 03:05 AM |
| Inserting text from a Userform into a Field in a paragraph in a paragraph in a word document | storemaz | Word VBA | 1 | 03-13-2020 08:11 AM |
Continuous Paragraph across two columns vs Parallel Column Paragraph
|
Pinesh | Word | 2 | 03-09-2018 04:24 PM |
Cross-reference to paragraph not updating when paragraph moves
|
windhoek2010 | Word | 1 | 09-15-2017 08:30 PM |