2
votes

I'm currently trying to work with complex tables in Microsoft Word. My problem is, those tables have fused cells and rows, and I'm not sure of how many rows or columns i'll have.

Here is a (stupid) example how the kind of tables i'll have example of complex table

I get my table thanks to a bookmark, and then proceed to stock the table in a Dim for easier access

Sub SetTable()
    Dim tb as Table
    Selection.GoTo What:=wdGoToBookmark, Name:="MyTable"
    Selection.MoveDown
    Set tb = Selection.Tables(1)
End Sub

Now, I'd use that table to write in several tables of a database. Let's say, I have a table "Destinations", a table "Ways" and a table "Time"

I'm kinda blocked there. With fused rows and columns, i cannot access a whole column or row. But as i don't know how many rows and columns i have (i could have, for example, 5 different ways for "Destination 1", or several distances in "Way 1")

enter image description here

I am a little lost on how i should try to work. Cell(x,y).Row doesn't work because several rows are fused, and it is the same with Column, so we get errors extremely easily

I was thinking of putting tables in cells that might get an unknown number of rows/columns, a bit like this

enter image description here

The Problem with this method is that the person that'll write in the document won't be me. Meaning, if he has to create a table each time there is a new line/column that requires it, chance is that it'll become a problem quickly. (I haven't found yet a method to put something in a given cell of a table at the creation of a new line, I'm also open on that point)

I was wondering if there are best practices to apply in this kind of case, and I am looking for advices too. If you already had to treat something similar to this, how did you do?
Thanks in advance for your answers

Cordially,
Zawarudio

Note : The example of table here is insanely stupid, and even I don't even know what it's talking about. It was just to put informations in the tables, and have absolutely no link with what I'm trying to do. If you were lost by the distances/times/whatever, sorry about that

2

2 Answers

1
votes

I had some vacations so I didn't work on that question before now.
I just found a way that I felt was relevant, so I come here to share my answer

Note that I only worked on an unknown number of merged rows at the moment, so this answer will only be about that, though I believe it is the same. Also note that I'm on Word 2010. I don't know if rows/column behavior changed in 2013 or will change in the future. (well, obviously)

The big problem was that a merged row cell will only have a value of the first row of the merged row. Let's take a simple example

a table with merged rows

This table has 2 rows and 2 columns. We fused the rows of the 1st column.
table.Rows.Count will return 2, so will table.Columns.count.
table.cell(1,1).Range.text will return the content of the merged rows.
We would like table.cell(2,1).Range.text to return the value of the merged row, but VBA tells us here that this value doesn't exist.

There is no problem with table.cell(1,2).Range.text and table.cell(2,2).Range.text.

With values, that means that our table with merged rows is pretty equals to that Table equivalence

Where each empty cell would generate an error 5941.

How to resolve the problem?

Sub ReadAllRows()
    Dim NbRows As Integer
    Dim NbColumns As Integer
    Dim i, j As Integer
    Dim SplitStr() As String

    Dim col1 as String
    Dim col2 as String
    Dim col3 as String
    Dim col4 as String

    'note : my table here is a public value that i get thanks to bookmarks
    NbRows = table.Rows.count
    NbColumns = table.Columns.count

    For i = 3 To NbRows
        'We put each value of each columns in a dim
        'We do that to remember previously entered row value if the application encounters an error
        'Because of merged rows, some cells on each row will not exist and return an error
        'When the application encounters an error, it just proceeds to next column
        'As previous existing value of this column was stocked in a Dim, we can get the full row at the end of the column loop
        For j = 1 To NbColumns
            On Error GoTo ErrorHandler

            SplitStr = Split(table.Cell(i, j).Range.Text, Chr(13))
            Select Case j
                Case 1:
                    col1 = SplitStr(0)
                Case 2:
                    col2 = SplitStr(0)
                Case 3:
                    col3 = SplitStr(0)
                Case 4:
                    col4 = SplitStr(0)

                'ect...
            End Select
NextRow:
        Next j

        'We have here all the values of the line
        MsgBox "col1: " & col1 & Chr(10) & _
            "col2: " & col2 & Chr(10) & _
            "col3: " & col3 & Chr(10) & _
            "col4: " & col4 & Chr(10)
    Next i

'This Error handler will skip the whole Select Case and thus will proceed towards next cell
ErrorHandler:
If Err.Number = 5941 Then
    Err.Clear
    Resume NextRow
End If

End Sub

That way, when a cell doesn't exist, that mean the row if merged. Meaning we want the last known value of the row. Since we skip the whole select when row is unknown, the value of the Dim isn't changed while we do get right the value of not merged rows.

This isn't rocket science, but I first began with a simple On Error Resume Next, and with that, non-existing rows simply had the value of last existing row, so I also had to work on a function that would try to get the good value for each cell of each row...

Note that I did things the ugly way here, but you can use a one dimensionnal arrays to stock an entire row the way Word is supposed to understand it, or you can even get a two dimensionnal array stocking your whole table in it a way Word understands

Well, I hope it helps someone, someday!

Cordially,
Zawarudio

0
votes

I think there must be an existing Q/A about this but I didn't find it using a quick search, so for now...

One thing you can do is iterate through the cells of the range of the table. Like this:

Sub iterTable()
Dim r As Range
Set r = ActiveDocument.Tables(1).Range
For i = 1 To r.Cells.Count
  Debug.Print r.Cells(i).RowIndex, r.Cells(i).ColumnIndex, r.Cells(i).Range.Text
Next
End Sub

As long as you have predefined texts that will allow you to detect your "Destination" groups, that should be enough for you to make progress...