1
votes

I have a Word VBA script that adds some headings and a table to the current selection. I'm now trying to get it to pull information from the table below and put it under the correct heading. The end goal is to take the information out of table format for better navigation, because Word's outline doesn't recognize headings inside tables.

I've only gotten as far as putting table content into string variables before I get run-time error 5941: The requested member of the collection does not exist. The debugger goes to this line:

strChildren = rngSource.Tables(1).Cell(Row:=2, Column:=4).Range.Text

The table has far more than two rows and four columns. To make sure the member of the collection existed, I used another script to give me the row and column for the current selection:

Sub CellRowColumn()
'For the current selection, shows a message box with the cell row and column.

With Selection.Cells(1)

        MsgBox ("Column = " & .ColumnIndex & vbCr & "Row = " & .RowIndex)

End With

End Sub

I ran this one in the cell I want to copy from, and it does show Row 2 & Column 4.

This is the code I'm using:

Sub ElementHeadings()
'With the current selection, adds the headings for each element in the 
'Elements and Attribute List (Description, Parent(s), and Child(ren)) and
'a table for attributes, with 3 columns, headed "Attribute
'Name", "Attribute Required?" and "Attribute Content")

Dim rngSelection As Range
Dim rngTable As Range
Dim rngHeading As Range
Dim rngSource As Range
Dim strCaption As String
Dim lngCaptionLength As Long

Dim strDescr As String
Dim strParents As String
Dim strChildren As String
Dim strVol As String
Dim strUsedIn As String


Set rngSelection = Selection.Range
'msgBox (rngSelection.Text)


With rngSelection

    .InsertAfter ("Description")
    .InsertParagraphAfter
    .Expand unit:=wdParagraph

    .InsertAfter ("Parent(s)")
    .InsertParagraphAfter
    .Expand unit:=wdParagraph

    .InsertAfter ("Child(ren)")
    .InsertParagraphAfter
    .Expand unit:=wdParagraph

    .InsertParagraphAfter
    .InsertParagraphAfter

    Set rngTable = .Paragraphs(5).Range

    .InsertAfter ("Volume & Chapter")
    .InsertParagraphAfter
    .Expand unit:=wdParagraph

    .InsertAfter ("Used In")
    .Expand unit:=wdParagraph

    .Style = "Heading 4"

'MsgBox (rngSelection.Text)

End With

ActiveDocument.Tables.Add Range:=rngTable, NumRows:=3, NumColumns:=3

With rngTable

    .Tables(1).Cell(1, 1).Range.Text = "Attribute Name"
    .Tables(1).Cell(1, 2).Range.Text = "Attribute Required?"
    .Tables(1).Cell(1, 3).Range.Text = "Attribute Content"

    .Select

    GenericMacros.TableFormat

    .Move unit:=wdParagraph, Count:=-1
    .Select

End With

rngSelection.Select

Set rngHeading = Selection.GoTo(what:=wdGoToHeading, Which:=wdGoToPrevious)
rngHeading.Expand unit:=wdParagraph

'MsgBox (rngHeading.Text)

rngTable.Select

strCaption = rngHeading.Text
lngCaptionLength = Len(strCaption)
strCaption = Left(strCaption, lngCaptionLength - 1)

Selection.InsertCaption Label:=wdCaptionTable, Title:=". <" _
    & strCaption & "> Attribute Table"

rngSelection.Select

Set rngSource = Selection.GoTo(what:=wdGoToTable, Which:=wdGoToNext)

rngSource.Expand unit:=wdTable

strDescr = rngSource.Tables(1).Cell(Row:=2, Column:=2).Range.Text
strParents = rngSource.Tables(1).Cell(Row:=2, Column:=3).Range.Text
strChildren = rngSource.Tables(1).Cell(Row:=2, Column:=4).Range.Text
strVol = rngSource.Tables(1).Cell(Row:=2, Column:=8).Range.Text
strUsedIn = rngSource.Tables(1).Cell(Row:=2, Column:=9).Range.Text

MsgBox ("strDescr = " & strDescr & vbCr & "strParents = " & strParents & _
    vbCr & "strChildren =" & strChildren & vbCr & "str3001Vol = " _
    & str3001Vol & "strUsedIn = " & strUsedIn)

End Sub

(This may end up being a SuperUser question rather than a Stack Overflow question, if the problem is the document rather than my code. Previously, I was having trouble copying and pasting from the table (copying text but not getting the option to paste it above), but that's no longer happening. So if there's not an apparent issue with the code, maybe it's document corruption or some other Word weirdness.)

Update: My source range contained the table I had just created, rather than the one I wanted to pull from, so I fixed the Selection.Goto that was creating rngSource.

2
Just found the error myself. My source range contained the table I had just created, rather than the one I wanted to pull from, so I fixed the Selection.Goto that was creating rngSource. Not sure if this is still a useful question to anyone else, so what's the etiquette here? Do I answer my own question and accept the answer, or should someone flag this for deletion? - Kelly Tessena Keck
It can go either way, as far as the etiquette is concerned. I'd like to address the problem you had, due to working with the current Selection. As you've discovered, this can be unreliable. It's much better to work with Word's objects as whenever possible. For example, when you create a table, Dim a variable, then assign to it when you create the table. That gives you a "handle" on the table, no matter what kind of editing takes place before it, later: Set tbl = ActiveDocument.Tables.Add(Range:=rngTable, NumRows:=3, NumColumns:=3). Then tbl.Cell(1,1).Range.Text and so on... - Cindy Meister
You're right! I thought working with a range would address that, but ranges also change when you edit them. (Selection.Goto was a workaround because the Range object doesn't seem to have a similar method, unless I'm missing something.) I hadn't thought to assign a variable to the table--that should work much better. The code you listed will work for the table I'm creating as part of the script, but I'll need to tweak it a little for the table of source info, which is pre-existing. - Kelly Tessena Keck
And I'll happily accept that as an answer, since it's more generally useful than my "tweak the range so it contains the right table". - Kelly Tessena Keck

2 Answers

3
votes

Good that you were able to track down where your code was failing. Working with the Selection object tends to be unreliable as it may not be where you're assuming (or where it was) when you wrote the code.

It's much better to work with Word's objects as whenever possible. For example, when you create a table, Dim a variable, then assign to it when you create the table. That gives you a "handle" on the table, no matter what kind of editing takes place before it, later:

Dim tbl as Word.Table
Set tbl = ActiveDocument.Tables.Add(Range:=rngTable, NumRows:=3, NumColumns:=3). 
tbl.Cell(1,1).Range.Text = "Attribute Name"
'and so on...

To pick up an existing table you need to be able to identify it. If you're certain of the position, then:

Set tbl = ActiveDocument.Tables([index value])

If this is a "template" kind of document that you set up and re-use you can bookmark the table (select the table and insert a bookmark, or click in the first cell and insert a bookmark), then:

Set tbl = ActiveDocument.Bookmarks("BookmarkName").Range.Tables(1)

In a similar vein, you can replace this:

rngHeading.Expand unit:=wdParagraph

with the following if you want to work with the paragraph, explicitly:

Dim para as Word.Paragraph
Set para = rngHeading.Paragraphs(1)

It may also help you to know you can "collapse" a Range (similar to pressing the Arrow key with a selection) to its start or end point. This is useful if you want to add something, format it, then add something else that should have different formatting... (as an alternative to using InsertAfter consecutively then going back and formatting things differently).

0
votes

I got something like OP, and after running below code:

Dim tbl As Word.Table: Set tbl = doc.Tables(2)
MsgBox tbl.Cell(1, 1).Range.Text

Which works on the idea that each table should have at least one cell in it, did notice that I was accessing the wrong table too ;-)

So, you may use that first to get sure.