【问题标题】:VBA WORD: find a paragraph that has a specific styleVBA WORD:查找具有特定样式的段落
【发布时间】:2020-04-15 20:52:47
【问题描述】:

我正在尝试在 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 文档,它是目录的第一行。 你知道为什么会这样吗?

【问题讨论】:

  • 只是关于编辑问题的有用提示。如果您有回复和其他答案,那么您不应该更改原始问题,而是应该添加它(请参阅我的回答),以便您对原始问题的任何 cmet 和回复仍然有意义。

标签: vba ms-word styles paragraph


【解决方案1】:

您提供的代码无法编译。它给出了一个错误

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 年 4 月 15 日更新,解决 TOC 问题

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

对象的默认成员是一个方法,如果给出的对象实例没有限定方法,则调用该方法。此功能可能会有所帮助,因为它可以简化代码,但可能会导致类似于我们遇到的奇怪错误。

在 Word 中,样式是具有许多属性和方法的对象。样式的默认方法是 NameLocal(参见 Word.Style 的对象浏览器),它返回一个包含字符串的 Variant(参见本地窗口中的样式类型)。 因此,即使 pStyle 被定义为 String 类型,VBA 强制转换允许将变体/字符串分配给 String pStyle,并且一切似乎都正常。

但是,在 TOC 字段的情况下,Word 似乎不会返回包装 TOC 字段的样式,而是返回“nothing”的值,即 TOC 没有样式。不能将任何内容(与 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 代码时消除了很多痛苦,因为它可以帮助您准确理解您在代码中所做的假设(您没有意识到自己所做的假设)......

【讨论】:

  • 并非如此。 "While Not pStyle "Heading 1" 是一个双重否定,所以你实际上是在说 "While pStyle = "Heading 1" 所以如果第一段不是"Heading 1",循环将不会做任何事情。我怀疑你的意思是“虽然 pStyle “标题 1”。你在 i = i + 1 的位置也有错误。它应该在“pStyle = ....”行之后。为什么?,因为你设置在开始循环之前将 i 设为 1,然后循环中的第一个操作是设置 i=i+1,因此您开始使用 i=2 的值测试 pstyle。如果您从未找到 a,您的代码也会产生错误“标题 1”
  • 我用修改后的函数为你更新了答案
  • 我理解代码,但我仍然不知道为什么当调试器达到 TOC 时会出现此错误。你知道为什么吗?
  • 目录后面有段落吗?
【解决方案2】:

Word 中的段落不可能没有段落样式,因此测试段落是否缺少任何样式根本没有意义。

此外,遍历所有段落的效率远低于使用 Find。例如,以下代码检索每个 Heading 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

更多代码,但速度更快。

【讨论】:

    【解决方案3】:

    改为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 方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-30
      • 2012-01-21
      相关资源
      最近更新 更多