View Full Version : [SOLVED:] Copy the out-most (primary/root/parent) table
KilpAr
08-08-2017, 04:49 AM
I have a table that might or might not have tables within it. I need to copy the root table no matter where the cursor is. I need to know which root table (there might several in the Word-document) I'm in and copy that one, regardless where in the table I am, inside table, inside cell, inside OLE object inside anything, still, the out-most table my cursor is in.
So with xml-type pseudo code (just to give the idea of what I'm after without loading an array of screenshots):
<tablea>
<tableaa>Spot 1
<tableaaa>Spot 2
</tableaaa>
</tableaa>
<tablec>Spot 3
</tablec>
</tablea>
<tableb>Spot 4
</tableb>
So if cursor is at Spot 1, copy tablea. If cursor is at Spot2, copy tablea. If cursor is at Spot 3, copy tablea. If cursor is at Spot 4, copy tableb.
I'm not sure.
Option Explicit
Sub test()
Dim t As Table
On Error Resume Next
Set t = Selection.Tables(1)
If Err.Number <> 0 Then Exit Sub
Do
Selection.Move wdCharacter, 1
Set t = Selection.Tables(1)
If Err.Number <> 0 Then
t.Range.Copy
Exit Do
End If
Loop
Documents.Add.Range.Paste
End Sub
KilpAr
08-08-2017, 07:06 AM
I'm not sure.
Option Explicit
Sub test()
Dim t As Table
On Error Resume Next
Set t = Selection.Tables(1)
If Err.Number <> 0 Then Exit Sub
Do
Selection.Move wdCharacter, 1
Set t = Selection.Tables(1)
If Err.Number <> 0 Then
t.Range.Copy
Exit Do
End If
Loop
Documents.Add.Range.Paste
End Sub
Looks like infinite loop to me.
Option Explicit
Sub test2()
Dim t As Table
Dim i As Long
On Error Resume Next
Set t = Selection.Tables(1)
If Err.Number <> 0 Then Exit Sub
Do
i = Selection.Move(wdCharacter, 1)
Set t = Selection.Tables(1)
If Err.Number <> 0 Then Exit Do
If i = 0 Then Exit Do
Loop
t.Range.Copy
Documents.Add.Range.Paste
End Sub
Option Explicit
Sub test3()
Dim t As Table
If Not Selection.Information(wdWithInTable) Then Exit Sub
Do
Selection.Move wdCharacter, 1
Set t = Selection.Tables(1)
If Not Selection.Information(wdWithInTable) Then Exit Do
Loop
t.Range.Copy
Documents.Add.Range.Paste
End Sub
gmaxey
08-08-2017, 07:59 AM
Sub Test()
Dim oTbl As Table
Set oTbl = fcnParentTable(1)
If Not oTbl Is Nothing Then oTbl.Select
End Sub
Function fcnParentTable(Optional lngNestLevel As Long = 0) As Table
Dim oTbl As Table
Dim lngNest As Long
Dim oRng As Range
'Pass 1 to return patriarch table, Pass 0 to return immediate parent.
'Pass numbers greater than 1 to return ancestors other than patriarch of deep nested tables.
On Error Resume Next
Set oTbl = Selection.Tables(1)
On Error GoTo 0
If Not oTbl Is Nothing Then
With oTbl
lngNest = .NestingLevel
Set oRng = .Range.Cells(1).Range.Characters(1)
End With
If lngNestLevel = 0 Then lngNestLevel = lngNest + 1
Do While lngNest > lngNestLevel
oRng.MoveStart Unit:=wdCharacter, Count:=-1
Set oRng = oRng.Tables(1).Range.Cells(1).Range.Characters(1)
lngNest = oRng.Tables(1).NestingLevel
Loop
If lngNestLevel <= lngNest Then
Set fcnParentTable = oRng.Tables(1)
End If
End If
Set oTbl = Nothing
Set oRng = Nothing
lbl_Exit:
Exit Function
End Function
KilpAr
08-09-2017, 01:38 PM
Hmm... I think there is some problem with my table, but both of the codes worked once I recreated the test document.
KilpAr
08-09-2017, 02:39 PM
I think the infinite loop has something to do with the inner-most table having a grand total of zero characters.
KilpAr
08-09-2017, 03:02 PM
Actually, let's make this a question: What does the codes posted here assume about the characters of the document/tables?
gmaxey
08-09-2017, 03:08 PM
Yes, that breaks it doesn't it. Try:
Sub Test()
Dim oTbl As Table
Set oTbl = fcnParentTable(3)
If Not oTbl Is Nothing Then oTbl.Select
End Sub
Function fcnParentTable(Optional lngNestLevel As Long = 0) As Table
Dim oTbl As Table
Dim lngNest As Long
Dim oRng As Range
'Pass 1 to return patriarch table, Pass 0 to return immediate parent.
'Pass numbers greater than 1 to return ancestors other than patriarch of deep nested tables.
On Error Resume Next
Set oTbl = Selection.Tables(1)
On Error GoTo 0
If Not oTbl Is Nothing Then
With oTbl
lngNest = .NestingLevel
Set oRng = .Range.Cells(Range.Cells.Count).Range
oRng.Collapse wdCollapseEnd
End With
If lngNestLevel = 0 Then lngNestLevel = lngNest + 1
Do While lngNest > lngNestLevel
Do
oRng.Move Unit:=wdCharacter, Count:=1
Loop While oRng.InRange(oTbl.Range)
Set oRng = oRng.Tables(1).Range.Cells(Range.Cells.Count).Range
oRng.Collapse wdCollapseEnd
Set oTbl = oRng.Tables(1)
lngNest = oRng.Tables(1).NestingLevel
Loop
If lngNestLevel <= lngNest Then
Set fcnParentTable = oRng.Tables(1)
End If
End If
Set oTbl = Nothing
Set oRng = Nothing
lbl_Exit:
Exit Function
End Function
gmaxey
08-09-2017, 03:13 PM
It assumed nothing. It was just broke code pure and simple.
KilpAr
08-09-2017, 03:15 PM
Ok, so a test case for you:
1) Create a new document without typing any text.
2) Create inside the document a new table with one cell, without characters.
3) Create inside the table a new table with one cell, without characters.
4) Place the cursor in the cell created in step 3.
5) Run the code.
Expected result is selecting the table created at step 2. Actual result is an "object required" exception at
Set oRng = .Range.Cells(Range.Cells.Count).Range
KilpAr
08-09-2017, 03:19 PM
And thank you a lot for the effort you've put into this!
gmaxey
08-09-2017, 03:20 PM
That is not the result I get here running your test. I get the expected result. The table created in step 2 and all of its content is selected.
gmaxey
08-09-2017, 03:22 PM
Sure. It is an interesting challenge.
KilpAr
08-09-2017, 03:45 PM
That is not the result I get here running your test. I get the expected result. The table created in step 2 and all of its content is selected.
Hmm... Where does the "Range" point to i.e. Range of what? And when you debug it, what does the "Range.Cells.Count" return?
Also, I don't think it should matter, but I have Word 2016.
edit. To be more specific, "Range" in "Range.Cells.Count" in "Set oRng = .Range.Cells(Range.Cells.Count).Range ", because I can split it to three rows like:
Dim i As Integer
i = Range.Cells.Count
Set oRng = .Range.Cells(i).Range
and it crashes on the second line.
gmaxey
08-09-2017, 04:06 PM
oTbl (I clarified that in this version). .Range.Cells.Count returns 1 in both cases using your example.
Works equally as well Word 2010/2016 here.
Option Explicit
Sub Test()
Dim oTbl As Table
Set oTbl = fcnParentTable(1)
If Not oTbl Is Nothing Then oTbl.Select
End Sub
Function fcnParentTable(Optional lngNestLevel As Long = 0) As Table
Dim oTbl As Table
Dim lngNest As Long
Dim oRng As Range
'Pass 1 to return patriarch table, Pass 0 to return immediate parent.
'Pass numbers greater than 1 to return ancestors other than patriarch of deep nested tables.
On Error Resume Next
Set oTbl = Selection.Tables(1)
On Error GoTo 0
If Not oTbl Is Nothing Then
With oTbl
lngNest = .NestingLevel
MsgBox .Range.Cells.Count 'Returns 1
Set oRng = .Range.Cells(.Range.Cells.Count).Range
oRng.Collapse wdCollapseEnd
End With
If lngNestLevel = 0 Then lngNestLevel = lngNest + 1
Do While lngNest > lngNestLevel
Do
oRng.Move Unit:=wdCharacter, Count:=1
Loop While oRng.InRange(oTbl.Range)
MsgBox oTbl.Range.Cells.Count 'Returns 1
Set oRng = oRng.Tables(1).Range.Cells(oTbl.Range.Cells.Count).Range
oRng.Collapse wdCollapseEnd
Set oTbl = oRng.Tables(1)
lngNest = oRng.Tables(1).NestingLevel
Loop
If lngNestLevel <= lngNest Then
Set fcnParentTable = oRng.Tables(1)
End If
End If
Set oTbl = Nothing
Set oRng = Nothing
lbl_Exit:
Exit Function
End Function
KilpAr
08-09-2017, 04:26 PM
I have no idea where the difference comes from, but this later one seems to work.
gmaxey
08-10-2017, 04:02 AM
Probably just sloppy coding. I don't normally use something like just "Range..." and it was a result of a copy paste. I suppose the compiler here figured it out and didn't on your end.
KilpAr
08-22-2017, 05:22 AM
I was able to break this one again :/
I created a document with a table that has a table that has two cells from which I select the later one and then try to select the immediate parent (passing zero as the parameter). So with pseudo-XML:
<table>
<table>
<cell>
</cell>
<cell> SELECTION HERE
</cell>
</table>
</table>
Does that work for you?
gmaxey
08-22-2017, 04:51 PM
Function fcnParentTable(Optional lngNestLevel As Long = 0) As Table
Dim oTbl As Table
Dim lngNest As Long
Dim oRng As Range
'Pass 1 to return patriarch table, Pass 0 to return immediate parent.
'Pass numbers greater than 1 to return ancestors other than patriarch of deep nested tables.
On Error Resume Next
Set oTbl = Selection.Tables(1)
oTbl.Select
On Error GoTo 0
If Not oTbl Is Nothing Then
With oTbl
lngNest = .NestingLevel
Set oRng = .Range.Cells(.Range.Cells.Count).Range
oRng.Collapse wdCollapseEnd
End With
If lngNestLevel = 0 Then
If lngNest > 1 Then
lngNestLevel = lngNest - 1
Else
lngNestLevel = 1
End If
End If
Do While lngNest > lngNestLevel
Do
oRng.Move Unit:=wdCharacter, Count:=1
Loop While oRng.InRange(oTbl.Range)
Set oTbl = oRng.Tables(1)
lngNest = oRng.Tables(1).NestingLevel
Loop
If lngNestLevel <= lngNest Then
Set fcnParentTable = oRng.Tables(1)
End If
End If
Set oTbl = Nothing
Set oRng = Nothing
lbl_Exit:
Exit Function
End Function
KilpAr
08-27-2017, 09:14 AM
Thanks!
With a bit of modifying (feel free to take it further), this seems to work:
Option Explicit
Sub Test()
Dim oTbl As Table
Dim lNL As Long
lNL = 2
Set oTbl = fcnParentTable(lNL)
If Not oTbl Is Nothing Then oTbl.Select
End Sub
Function fcnParentTable(Optional ByVal lngNestLevel As Long = 0) As Table
Dim oTbl As Table
Dim lngNest As Long
Dim oRng As Range
'Pass 1 to return patriarch table, Pass 0 to return immediate parent.
'Pass numbers greater than 1 to return ancestors other than patriarch of deep nested tables.
On Error Resume Next
Set oTbl = Selection.Tables(1)
oTbl.Select
If lngNestLevel = 0 Then
Set fcnParentTable = oTbl
Exit Function
End If
On Error GoTo 0
If Not oTbl Is Nothing Then
With oTbl
lngNest = .NestingLevel
Set oRng = .Range.Cells(.Range.Cells.Count).Range
oRng.Collapse wdCollapseEnd
End With
If lngNestLevel = 0 Then
If lngNest > 1 Then
lngNestLevel = lngNest - 1
Else
lngNestLevel = 1
End If
End If
Do While lngNest > lngNestLevel
Do
oRng.Move Unit:=wdCharacter, Count:=1
Loop While oRng.InRange(oTbl.Range)
Set oTbl = oRng.Tables(1)
lngNest = oRng.Tables(1).NestingLevel
Loop
If lngNestLevel <= lngNest Then
Set fcnParentTable = oRng.Tables(1)
End If
Set oTbl = Nothing
Set oRng = Nothing
End If
lbl_Exit:
Exit Function
End Function
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.