Copy certain table row from one table to another - vba

I have a word document with several tables.
The first table in the document is supposed to be a summary table containing copies of certain rows of the other tables in the document.
How would I have to tackle the automated population of the summary table with the specific rows of the other tables?
I assume there is no built in function and I'd have to use VBA.
I'd think I have to mark the rows to be copied with some marker (e.g.: "###") for the code to detect. Then the code would have to go through the document, find all the "###", mark them as "dealt with" (e.g., with "##-") and copy/append the respective row into the first table, and then loop through the document again.

You can mark the row(s) using bookmarks; name them with a special prefix. Loop through the bookmarks collection, check the prefix and if it's the correct one, append the row to the sumamry table. You can delete the bookmark in the process, if you wish.
Here's some sample code which works for me in a quick test. The boomkark prefix is "tbl"; if you don't want it to be visible to the user, start the name with an underscore (_).
The target range for the summary table is at the start of the document. Change the assignment to the rng object if you need it elsewhere.
Dim doc As word.Document
Dim rng As word.Range
Dim rwSource As word.Row
Dim bkm As word.Bookmark
Set doc = ActiveDocument
Set rng = doc.Range(0, 0)
For Each bkm In doc.Bookmarks
If Left(bkm.Name, 3) = "Tbl" Then
rng.FormattedText = bkm.Range.FormattedText
rng.Collapse wdCollapseEnd
End If
Next
Note: I tried using REF fields to pick up the bookmarked rows, without needing VBA. This inserted an empty row between each REF field, however, so I don't think it can be done without VBA.

Related

Move the line before the table along with the table if the line appears on the page preceding the table

We have captions for tables in our Word documents that do not behave like captions and act as "Normal" word styles. These captions sometimes appear on the preceding page of the table.
There are hundreds of documents and may be a million instances of captions in those documents. The documents were imported from an old source system to a new source system using a script, which didn't do the job as expected.
I would like if such captions exist on the preceding page, they are removed from the page and shown before the table on the same page.
The logic that I want to use:
Find a table
Find the page number of the table
Find the first instance of "Normal" paragraph style just before the table
Find the page number of the "Normal" Paragraph style
Only if the page number of the first instance of the "Normal" Paragraph style is less than the page number of the table by 1, then move the entire instance to the next page just before the table
Do this for all tables.
So far I could only manage to write this code:
Dim tblNew As Table
Dim oPara As Paragraph
Dim oRng As Range
Dim PageNo As Integer
Dim PageNoCap As Integer
For Each tblNew In ActiveDocument.Tables
PageNo = tblNew.Range.Information(wdActiveEndPageNumber)
I would approach this by fixing the incorrect use of styles. The code below applies the Caption style to demonstrate the principle.
Using code to compensate for not using Word correctly in the first place is a bad idea, but if you have existing documents that you need to correct could be a useful temporary measure.
Sub ApplyStyleToTableCaptions()
Dim tbl As Table
Dim oRng As Range
With ActiveDocument.Styles(wdStyleCaption).ParagraphFormat
If Not .KeepWithNext Then .KeepWithNext = True
End With
For Each tbl In ActiveDocument.Tables
Set oRng = tbl.Range
oRng.Collapse wdCollapseStart
oRng.Move wdParagraph, -1
oRng.Paragraphs(1).Range.Style = wdStyleCaption
Next tbl
End Sub

VBA Table in Merged Letter

I have an Excel Sheet with a lot of customer Data. All customers have common data (address, name etc.) that I implemented as simple mergefields. Some Customers have multiple Datasets that should be added as a Table at the end of the merged letter.
To find the Data from my excel Sheet I already came up with the following code.
noInt is the number of customers while noData is the number of different datasets (all customers together, some multiples). exWb is the excel workbook my data comes from and the data I want to display in the table lays in columns 5 to 9.
For i = 2 To noInt
For k = 2 To noData
If exWb.Sheets("Table1").Cells(k, 1) = exWb.Sheets("Table2").Cells(i, 1) Then
For j = 5 To 9
Insert into Table exWb.Sheets("Table1").Cells(k, j)
Next j
End If
Next k
Next i
Now my questions:
How can I insert this data into a newly created table after the placeholder "insert_table_here"?
How can I make sure that for every letterin the mail merge series there is only the data of the customer the letter is about included in this table?
To find a solution to this, I already thought about if there was maybe a function that gives the current "Mail Merge Number". In that case I could compare the field (MailMergeNumber, 1) with (k,1) to only show the results that include the current customer.
Example to make it more understandable:
Dear Mr A,
...
Table of items Mr. A bought
-End of document-
Dear Mr. B,
...
Table of items Mr. B bought
-End of document-
And so on...
If you're creating Word documents from a template (that's generally the easiest way I've found to do it), you can add a table to the template document with the header rows you need, and 1 blank row for the data. Then, after populating the basic mergefields, you could loop through the current customer fields, adding new rows to the Word table as you went. Something like this:
Dim exWs as Excel.Worksheet
Dim CurrentCustomerFirstCell as Excel.Range
Dim CurrentCustomerActiveCell as Excel.Range
Dim EmpRowOffset as integer
Dim wdDoc as Word.Document
Dim wdTable as Word.Table, wdCell as Word.Cell
' set up your existing references, including (I assume) to the Word document you're updating
set exWs = exWb.Sheets("Table1")
' initialize row for current employee
CurrentCustomerFirstCell = exWs.Cells(2,1)
do while CurrentCustomerFirstCell.Row <= noData ' consider renaming noData to somthing like "numberOfRows"
' populate basic mergefields
wdDoc.Fields(1).Result.Text = CurrentCustomerFirstCell.Value
' etc.
' populate table in Word document
set wdTable = wdDoc.Tables(1)
EmpRowOffset = 0
set CurrentCustomerActiveCell = CurrentCustomerFirstCell.Offset(Rowoffset:=EmpRowOffset)
set wdTable = wdDoc.Tables(1)
do while CurrentCustomerActiveCell.Value = CurrentCustomerFirstCell.Value
' this code would update the first "data" row in the existing Word table
' to the 6th column of the active employee row
set wdCell = wdTable.Cell(Row:=2 + EmpRowOffset, Column:=1)
wdCell.Range.Text = _
CurrentCustomerActiveCell.Offset(columnoffset:=5).Value
wdTable.Rows.Add
EmpRowOffset = EmpRowOffset + 1
set CurrentCustomerActiveCell = CurrentCustomerFirstCell.Offset(RowOffset:=EmpRowOffset)
Loop
' now that we're finished processing the employee, update CurrentCustomerFirstCell
set CurrentCustomerFirstCell = CurrentCustomerActiveCell
loop
You can use Word's Catalogue/Directory Mailmerge facility for this (the terminology depends on the Word version). To see how to do so with any mailmerge data source supported by Word, check out my Microsoft Word Catalogue/Directory Mailmerge Tutorial at:
http://www.msofficeforums.com/mail-merge/38721-microsoft-word-catalogue-directory-mailmerge-tutorial.html
or:
http://www.gmayor.com/Zips/Catalogue%20Mailmerge.zip
The tutorial covers everything from list creation to the insertion & calculation of values in multi-record tables in letters. Do read the tutorial before trying to use the mailmerge document included with it.
Depending on what you're trying to achieve, the field coding for this can be complex. However, since the tutorial document includes working field codes for all of its examples, most of the hard work has already been done for you - you should be able to do little more than copy/paste the relevant field codes into your own mailmerge main document, substitute/insert your own field names and adjust the formatting to get the results you desire. For some worked examples, see the attachments to the posts at:
http://www.msofficeforums.com/mail-merge/9180-mail-merge-duplicate-names-but-different-dollar.html#post23345
http://www.msofficeforums.com/mail-merge/11436-access-word-creating-list-multiple-records.html#post30327
Another option would be to use a DATABASE field in a normal ‘letter’ mailmerge main document and a macro to drive the process. An outline of this approach can be found at:
http://answers.microsoft.com/en-us/office/forum/office_2010-word/many-to-one-email-merge-using-tables/8bce1798-fbe8-41f9-a121-1996c14dca5d
Conversely, if you're using a relational database or, Excel workbook with a separate table with just a single instance of each of the grouping criteria, a DATABASE field in a normal ‘letter’ mailmerge main document could be used without the need for a macro. An outline of this approach can be found at:
https://answers.microsoft.com/en-us/msoffice/forum/msoffice_word-mso_winother-mso_2010/mail-merge-to-a-word-table-on-a-single-page/4edb4654-27e0-47d2-bd5f-8642e46fa103
For a working example, see:
http://www.msofficeforums.com/mail-merge/37844-mail-merge-using-one-excel-file-multiple.html
Alternatively, you may want to try one of the Many-to-One Mail Merge add-ins, from:
Graham Mayor at http://www.gmayor.com/ManyToOne.htm; or
Doug Robbins at https://onedrive.live.com/?cid=5AEDCB43615E886B&id=5AEDCB43615E886B!566

Access table object based on current paragraph

I am iterating through all paragraphs in a document and want to identify their indent level. While this works most of the time using Paragraph.Format.LeftIndent, it does not work for paragraphs within tables. In particular in cases when the paragraph itself is not indented but the table has a left indent.
Is there any way to navigate from the currelty selected paragraph to the table where it is located in? I know there is the property "wdWithInTable" but this is not enough for me because I need the Table object to check its LeftIndent property.
Go get table from current selection use this logic:
Sub GetTable()
Dim currentTable As Table
Set currentTable = Selection.Tables(1)
'test purpose only
Debug.Print currentTable.Rows.Count, currentTable.Columns.Count
'and to get table indention try with this
Debug.Print currentTable.Range.ParagraphFormat.LeftIndent
End Sub

Move Word Table Row Without Using Clipboard

I have a table in Word and I need to move certain rows to the bottom of the table using VBA.
I know I can do this by cutting and pasting (myTable.Rows(2).Select ... Selection.Cut ... etc.) but I don't want to have to use the clipboard.
Although I can manually highlight the row and drag it using the mouse, when I record a macro, the drag is not allowed and there does not seem to be any obvious menu instruction to carry out the equivalent.
The only other thing I can think of doing is (using VBA):
Add a new row at the bottom
Replicate the data in the new row from the one to be moved
Delete the original row
A "sexier" solution to this must be out there ;)
Here are two things you could explore:
Use FormattedText:
Sub moverow4table1toend()
Dim source As Word.Range
Dim target As Word.Range
Dim t As Word.Table
Set t = ActiveDocument.Tables(1)
Set target = t.Range
target.Collapse wdCollapseEnd
Set source = t.Rows(4).Range
target.FormattedText = source.FormattedText
source.Rows(1).Delete
Set source = Nothing
Set target = Nothing
Set t = Nothing
End Sub
Or perhaps if you are actually doing something more like sorting, you could add a column (assuming you haven't reached the limit, populate it with the destination row numbers, sort the table, and remove the column.

Distinguishing Table of Contents in Word document

Does anyone know how when programmatically iterating through a word document, you can tell if a paragraph forms part of a table of contents (or indeed, anything else that forms part of a field).
My reason for asking is that I have a VB program that is supposed to extract the first couple of paragraphs of substantive text from a document - it's doing so by iterating through the Word.Paragraphs collection. I don't want the results to include tables of contents or other fields, I only want stuff that a human being would recognize as a header, title or a normal text paragraph. However it turns out that if there's a table of contents, then not only the table of contents itself but EVERY line in the table of contents appears as a separate item in Word.Paragraphs. I don't want these but haven't been able to find any property on the Paragraph object that would allow me to distinguish and so ignore them (I'm guessing I need the solution to apply to other field types too, like table of figures and table of authorities, which I haven't yet actually encountered but I guess potentially would cause the same problem)
Because of the limitations in the Word object model I think the best way to achieve this would be to temporarily remove the TOC field code, iterate through the Word document, and then re-insert the TOC. In VBA, it would look like this:
Dim doc As Document
Dim fld As Field
Dim rng As Range
Set doc = ActiveDocument
For Each fld In doc.Fields
If fld.Type = wdFieldTOC Then
fld.Select
Selection.Collapse
Set rng = Selection.Range 'capture place to re-insert TOC later
fld.Cut
End If
Next
Iterate through the code to extract paragraphs and then
Selection.Range = rng
Selection.Paste
If you are coding in .NET this should translate pretty closely. Also, this should work for Word 2003 and earlier as is, but for Word 2007/2010 the TOC, depending on how it is created, sometimes has a Content Control-like region surrounding it that may require you to write additional detect and remove code.
This is not guaranteed, but if the standard Word styles are being used for the TOC (highly likely), and if no one has added their own style prefixed with "TOC", then it is OK. This is a crude approach, but workable.
Dim parCurrentParagraph As Paragraph
If Left(parCurrentParagraph.Format.Style.NameLocal, 3) = "TOC" Then
' Do something
End If
What you could do is create a custom style for each section of your document.
Custom styles in Word 2003 (not sure which version of Word you're using)
Then, when iterating through your paragraph collection you can check the .Style property and safely ignore it if it equals your TOCStyle.
I believe the same technique would work fine for Tables as well.
The following Function will return a Range object that begins after any Table of Contents or Table of Figures. You can then use the Paragraphs property of the returned Range:
Private Function GetMainTextRange() As Range
Dim toc As TableOfContents
Dim tof As TableOfFigures
Dim mainTextStart As Long
mainTextStart = 1
For Each toc In ActiveDocument.TablesOfContents
If toc.Range.End > mainTextStart Then
mainTextStart = toc.Range.End + 1
End If
Next
For Each tof In ActiveDocument.TablesOfFigures
If tof.Range.End > mainTextStart Then
mainTextStart = tof.Range.End + 1
End If
Next
Set GetMainTextRange = ActiveDocument.Range(mainTextStart, ActiveDocument.Range.End)
End Function