VBA WORD: 查找具有特定样式的段落

VBA WORD: find a paragraph that has a specific style

我正在尝试在 VBA 中编写代码,我将在其中获取具有特定样式的段落的编号(假设为标题 1)。 我正在经历一个循环,不幸的是我收到这样的错误:

"Object variable or With block variable not set"

这是我的代码:

Public Function FindParagraph(ByVal pStyle As String) As Integer
    Dim doc As Document
    Dim pNum As Integer
    Set doc = ActiveDocument

    For pNum = 1 To doc.Paragraphs.Count
        Debug.Print pNum, doc.Paragraphs(pNum).Range.Style
        If doc.Paragraphs(pNum).Range.Style = pStyle Then
            FindParagraph = pNum
            Exit For
        End If
    Next pNum
End Function

Sub DoSth()
    Dim i As Integer
    i = FindParagraph("Heading 1")
    Debug.Print i
End Sub

调试器显示问题出在这一行: pStyle = doc.Paragraphs(i).Range.Style 实际上,我正在查看我的 Word 文档,它是 Table 目录的第一行。 你知道为什么会这样吗?

您提供的代码无法编译。它在

处给出错误
While Not (IsEmpty(pStyle))

因为方法 IsEmpty 只能用于 Variant 类型,而您分配给 pStyle 的类型是字符串。为了实现您的意图,您需要将此行更改为

While Not pStyle = vbNullString

已更新以提供修改后的功能

Sub TestFindParagraph()

    Dim IsFound As Boolean
    IsFound = FindParagraph(ActiveDocument.StoryRanges(wdMainTextStory), "Heading 1")

End Sub


Public Function FindParagraph(ByVal SearchRange As Word.Range, ByVal ParaStyle As String) As Long

    Dim ParaIndex As Long
    For ParaIndex = 1 To SearchRange.Paragraphs.Count

        If doc.Paragraphs(ParaIndex).Range.Style = ParaStyle Then

            FindParagraph = ParaIndex
            Exit Function

        End If

    Next

End Function

更新 2020-Apr-15 解决目录问题

当段落是 Table 的内容字段时,OP 和我自己发布的代码都失败了。发生此故障是因为 VBA 个对象的默认成员功能。

对象的默认成员是在没有限定方法的情况下给定对象实例时调用的方法。此功能有助于简化代码,但可能会导致类似于我们遇到的奇怪错误。

在 Word 中,样式是具有大量属性和方法的对象。 Style 的默认方法是 NameLocal(请参阅 Word.Style 的对象浏览器),其中 return 是一个包含字符串的 Variant(请参阅本地 windows 中样式的类型)。 因此,即使 pStyle 被定义为 String 类型,VBA 强制转换也允许将 variant/string 分配给 String pStyle,并且一切似乎都正常。

但是,对于 TOC 字段,Word 似乎没有 return 包装 TOC 字段的样式,而是 returns 'nothing' 的值,即不是目录的样式。不能将任何内容(与 vbNullString 不同)分配给字符串,因此会发生错误。

上面遇到的问题似乎有两种解决方案。

  1. 更改代码以对我们需要的信息使用正确的语法。即 Style.NameLocal。不幸的是,这个解决方案也会失败,因为我们不能在什么都不是的对象上调用方法 (NameLocal)。

  2. 将 pStyle 的变量类型从 String 更改为 Variant。变体类型可以保存对象,因此可以保存当段落是 TOC 字段时生成的任何值。

解决方案 2 似乎工作正常,但从纯粹主义者的角度来看,您将有一个中间变体变量,它捕获 Style 的结果,然后在将字符串值或 vbNullString 分配给 pStyle 之前测试该变体。

最终更新 2020 年 4 月 15 日

不幸的是,我因为一些紧急工作被 F 夫人叫走了,所以忘记添加最后的内容。

可以轻松避免默认成员陷阱。这要归功于 Rubberduck 的人们所做的出色工作。

http://rubberduckvba.com/

VBA 的 Rubberduck 插件(免费)作为其众多才能之一,对 VBA 进行了更严格的代码分析(代码检查)。其中一项检查是在代码中使用默认成员的任何地方发出警告。 Rubberduck 确实消除了编写 VBA 代码时的大量痛苦,因为它可以帮助您准确理解您在代码中所做的假设(您没有意识到自己所做的假设)...

Word中的段落不可能没有段落样式,所以测试一个段落是否完全没有任何样式是没有意义的。

此外,循环遍历所有段落的效率远低于使用“查找”。例如,以下代码检索每个标题 1 的段落索引#:

Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range, i As Long
With ActiveDocument.Range
  Set Rng = .Duplicate
  With .Find
    .ClearFormatting
    .Replacement.ClearFormatting
    .Text = ""
    .Replacement.Text = ""
    .Style = wdStyleHeading1
    .Format = True
    .Forward = True
    .Wrap = wdFindStop
    .MatchWildcards = False
    .Execute
  End With
  Do While .Find.Found
    i = i + 1
    Rng.End = .Duplicate.End
    MsgBox Rng.Paragraphs.Count
    'The next If ... End If block is only needed if the Found content might be in a table
    If .Information(wdWithInTable) = True Then
      If .End = .Cells(1).Range.End - 1 Then
        .End = .Cells(1).Range.End
        .Collapse wdCollapseEnd
        If .Information(wdAtEndOfRowMarker) = True Then
          .End = .End + 1
        End If
      End If
    End If
    'The next line is only needed if the Found content might include the document's final paragraph break
    If .End = ActiveDocument.Range.End Then Exit Do
    .Collapse wdCollapseEnd
    .Find.Execute
  Loop
End With
Application.ScreenUpdating = True
MsgBox i & " instances found."
End Sub

更多代码,但速度更快。

而是for i=1 to ....Count 尝试 for each 循环以获得更好的效率

Private Sub StylesCount()
    Dim p As Paragraph
    Dim story As Range
    Dim counter As Long

    For Each story In ActiveDocument.StoryRanges
        For Each p In story.Paragraphs
            If StrComp(p.Style, "Heading 1", vbTextCompare) = 0 Then
                counter = counter + 1
            End If
        Next p
    Next story

    Debug.Print counter

End Sub

它适用于段落样式。对于字符样式,您应该使用 .find 方法。