How To Add An Unknown Number of Paragraphs & Tables When Automating Word

I am trying to change a working Word automation  subroutine to allow a variable number of paragraphs and tables and have run into some difficulty. I had hoped to be able to define the range, paragraph and table objects as arrays and then define them as part of the Word document as I looped through the data. The problem is that some of the items are members of  System.Array so that approach will not work. I would like suggestions on how to address a variable number of paragraphs and tables when automating Word. My several E-E searches have been unsuccessful.
Public Sub WordPrintTWSampleSize(ByVal gNbrCategories As Short)
            Dim oWord As Object
            Dim oDoc As Object
            Dim oPara1 As Object
            Dim oPara2 As Object
            Dim oRng() As Object
            Dim DateTime As Date
            Dim iCatInProcess As Short = 0
            Dim oPar() As Object
            Dim oTable(iCatInProcess)() As Object
            ReDim oPar(gNbrCategories - 1)
            ReDim oTable(iCatInProcess)(gNbrCategories - 1)
            ReDim oRng(gNbrCategories - 1)
            DateTime = Now
            'Start Word and open the document template.
            oWord = CreateObject("Word.Application")
            oDoc = oWord.Documents.Add
            oPara1 = oDoc.Content.Paragraphs.Add
            oPara2 = oDoc.Content.Paragraphs.Add
            oWord.Visible = True
            oDoc.PageSetup.Orientation = 1 ' 1 is landscape, 0 is portrait
            oDoc.PageSetup.TopMargin = 1.0#
            oDoc.PageSetup.BottomMargin = 0.75
            'Insert a paragraph at the beginning of the document.
            oPara1 = oDoc.Content.Paragraphs.Add
            oPara1.Range.Font.Size = 8
            oPara1.Range.Text = Space(283) & DateTime & vbCrLf
            oPara1.Range.Font.Size = 12
            oPara1.Range.ParagraphFormat.Alignment = 1
            oPara1.Range.Text = "Two Way Stratification and Sample Size Report" & vbCr
            If gParentCompanyName.Length > 0 Then
                oPara1.Range.Text = Trim(gParentCompanyName) & vbCr & vbCr
            End If
            Dim R, c As Short
            For iCatInProcess = 0 To gNbrCategories - 1
                oPar(iCatInProcess) = oDoc.Content.Paragraphs.Add
                oPar(iCatInProcess).Range.Text = Trim(TWAuditInfo(iCatInProcess).CompanyName) & vbCr
                oPar(iCatInProcess).Range.Bold = False
                oPar(iCatInProcess).Format.SpaceAfter = 12
                '  Insert a TWAuditInfo(icatinprocess).nbrstrata row by 8 column table here and make the first row
                '  bold.  Fill the other cells with data
                oRng(iCatInProcess) = oDoc.bookmarks.item("\endofdoc").range
                oTable(iCatInProcess) = oDoc.Tables.Add(oRng(iCatInProcess), TWAuditInfo(iCatInProcess).NbrStrata + 2, 7, DefaultTableBehavior:=1, AutoFitBehavior:=1)
                oTable(iCatInProcess)(iCatInProcess).Range.ParagraphFormat.SpaceAfter = 6
                oTable(iCatInProcess)(iCatInProcess).Range.Font.Size = 11
                oTable(iCatInProcess).Rows(1).Range.Font.Bold = True
                R = 1
                c = 1
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(0.75)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Stratum"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                c = 2
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(1)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Lower Boundary"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                c = 3
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(1)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Upper Boundary"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                c = 4
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(1)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Stratum Size"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                c = 5
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(1.5)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Stratum Total"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                c = 6
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(1)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Standard Deviation"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                c = 7
                oTable(iCatInProcess).Columns(c).Width = oWord.InchesToPoints(1)
                oTable(iCatInProcess).Cell(R, c).Range.Text = "Sample Size"
                oTable(iCatInProcess).Cell(R, c).Range.ParagraphFormat.Alignment = 1
                For R = 0 To TWAuditInfo(iCatInProcess).NbrStrata - 1
                    oTable(iCatInProcess).Rows(R + 2).Range.Font.Bold = False
                    oTable(iCatInProcess).Cell(R + 2, 1).Range.Text = (R + 1).ToString
                    oTable(iCatInProcess).Cell(R + 2, 1).Range.ParagraphFormat.Alignment = 1
                    oTable(iCatInProcess).Cell(R + 2, 2).Range.Text = TWStratSumm(iCatInProcess).Lower(R).ToString("$#,###,##0.00")
                    oTable(iCatInProcess).Cell(R + 2, 2).Range.ParagraphFormat.Alignment = 2
                    oTable(iCatInProcess).Cell(R + 2, 3).Range.Text = TWStratSumm(iCatInProcess).Upper(R).ToString("$#,###,##0.00")
                    oTable(iCatInProcess).Cell(R + 2, 3).Range.ParagraphFormat.Alignment = 2
                    oTable(iCatInProcess).Cell(R + 2, 4).Range.Text = TWAuditInfo(iCatInProcess).Nh(R).ToString("##,##0")
                    oTable(iCatInProcess).Cell(R + 2, 4).Range.ParagraphFormat.Alignment = 2
                    oTable(iCatInProcess).Cell(R + 2, 5).Range.Text = TWAuditInfo(iCatInProcess).Th(R).ToString("$##,##0.00")
                    oTable(iCatInProcess).Cell(R + 2, 5).Range.ParagraphFormat.Alignment = 2
                    oTable(iCatInProcess).Cell(R + 2, 6).Range.Text = TWAuditInfo(iCatInProcess).Sh(R).ToString("#,##0.00")
                    oTable(iCatInProcess).Cell(R + 2, 6).Range.ParagraphFormat.Alignment = 2
                    oTable(iCatInProcess).Cell(R + 2, 7).Range.Text = TWAuditInfo(iCatInProcess).NHh(R).ToString("###,##0")
                    oTable(iCatInProcess).Cell(R + 2, 7).Range.ParagraphFormat.Alignment = 2
                Next R
                R = TWAuditInfo(iCatInProcess).NbrStrata + 2
                oTable(iCatInProcess).Cell(R, 3).Range.Text = "Totals"
                oTable(iCatInProcess).Cell(R, 3).Range.ParagraphFormat.Alignment = 2
                oTable(iCatInProcess).Cell(R, 4).Range.Text = TWAuditInfo(iCatInProcess).NN.ToString("###,##0")
                oTable(iCatInProcess).Cell(R, 4).Range.ParagraphFormat.Alignment = 2
                oTable(iCatInProcess).Cell(R, 5).Range.Text = TWAuditInfo(iCatInProcess).TotalInvoices.ToString("$###,###,##0.00")
                oTable(iCatInProcess).Cell(R, 5).Range.ParagraphFormat.Alignment = 2
                oTable(iCatInProcess).Cell(R, 7).Range.Text = TWAuditInfo(iCatInProcess).TotalSamsize.ToString("###,##0")
                oTable(iCatInProcess).Cell(R, 7).Range.ParagraphFormat.Alignment = 2
            Next iCatInProcess
        Catch ex As Exception
        End Try
    End Sub

Open in new window

Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Hi, just a first thought : you will get better with the objects if you use early binding instead of late binding. (Add a reference to Microsoft.Office.Interop.Word)

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Could you explain in more detail the data you want to drop into your document. Reading over your question it sounds as though you can drop each paragraph into an arraylist and then send each one in turn to the Word Document once you've created it. Are the paragraphs and table entries seperate things? If there's more to it than that let me know!

Also i've included some tried & tested code below for creating and adding to a Word Document. When you 'Add()' the document, you can specify a template if you want, which would save you having to insert all the text you wanted. You can also use find & replace to add text to drop in dynamic data, but it might not be appropriate for this project.

Reply back if you have more information
' Make sure this is at the top of your page
Imports Word = Microsoft.Office.Interop.Word
Public Class WordDocBuilder
Private ParaArray As New ArrayList
    Sub CreateParaArray()
        ' You can add the arraylist data how you like
        ' eg with a database, but i've just done it
        ' statically for an example.
        ParaArray.Add("Paragraph1 Text")
        ParaArray.Add("Paragraph2 Text")
        ParaArray.Add("Paragraph3 Text")
        ParaArray.Add("Paragraph4 Text")
        ParaArray.Add("Paragraph5 Text")
    End Sub
    Sub CreateWordDoc()
        'Create The Application
        Dim oWord As Word.Application
        oWord = New Word.Application
        'Create The Document
        Dim oDoc As Word.Document
        oWord.Visible = True
        oDoc = oWord.Documents.Add()
        Dim ArrItem As String
        For Each ArrItem In ParaArray
            ' You could also fill table data here
        ' This is the find and replace method - 
        ' Just stick <<date>> into you template
        'oDoc.Content.Find.Execute(FindText:="<<date>>", ReplaceWith:=Now.ToShortDateString, Replace:=Word.WdReplace.wdReplaceAll)
        ' Or Save...
        'Dim StrFileName As String
        'StrFileName = "Test.doc"
    End Sub
End Class

Open in new window

rkulpAuthor Commented:
vb jonas & ianmair329:

Thanks for your suggestions. I have to use late binding since I don't know which version of Office any particular user is using. Otherwise, unlike VB6, I would need to compile a different version for each different version of Office.
I thought of an array list but could not figure out how to implement it. The paragraphs for the individual categories are the same except for the data itself, so that may work fine. The tables all have the same number of columns but have different numbers of rows. Each row is filled from a structure which is an item in an array list. The structures have all the statistical information for a single audit. I will investigate using a template for the column structure provided I can handle the number of rows.
The big problem is that one time the document could have just two tables and another time it may be ten. I think I can put the associated paragraph into the table so all I have to work out is adding a variable number of tables. Is there a way to create the table in a separate subroutine and then insert it into the primary document?  As you can tell, Word automation is essentially foreign territory for me.
Learn Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

rkulpAuthor Commented:
vb jonas & ianmair329:

I figured it out this afternoon. Dimensioning the paragraphs in an array of objects works fine. This does not work for tables because it is not necessary. Tables are a collection in a Word document so all that is necessary is to dimension one table as an object and then use that in the For . . . Next loop. It works very well.
The paragraphs are only titles so I am going to try to put them in a title row of the table and do away with the paragraphs in the For . . . Next loops. I am still trying to figure out how to select only the first row and then merge the cells. If you can help with that, I would appreciate it.
I am going to close this question and split the points evenly. Thanks for your suggestions.
rkulpAuthor Commented:
Got the merge, too:

                Dim oCell as Word.Cell              
                oCell = oTable.cell(1, 1)
                oTable.cell(1, 7).merge(oCell)
Happy to help - and glad you sorted that merge! Good luck with the rest of your project
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.