DEVTOME.COM HOSTING COSTS HAVE BEGUN TO EXCEED 115$ MONTHLY. THE ADMINISTRATION IS NO LONGER ABLE TO HANDLE THE COST WITHOUT ASSISTANCE DUE TO THE RISING COST. THIS HAS BEEN OCCURRING FOR ALMOST A YEAR, BUT WE HAVE BEEN HANDLING IT FROM OUR OWN POCKETS. HOWEVER, WITH LITERALLY NO DONATIONS FOR THE PAST 2+ YEARS IT HAS DEPLETED THE BUDGET IN SHORT ORDER WITH THE INCREASE IN ACTIVITY ON THE SITE IN THE PAST 6 MONTHS. OUR CPU USAGE HAS BECOME TOO HIGH TO REMAIN ON A REASONABLE COSTING PLAN THAT WE COULD MAINTAIN. IF YOU WOULD LIKE TO SUPPORT THE DEVTOME PROJECT AND KEEP THE SITE UP/ALIVE PLEASE DONATE (EVEN IF ITS A SATOSHI) TO OUR DEVCOIN 1M4PCuMXvpWX6LHPkBEf3LJ2z1boZv4EQa OR OUR BTC WALLET 16eqEcqfw4zHUh2znvMcmRzGVwCn7CJLxR TO ALLOW US TO AFFORD THE HOSTING.

THE DEVCOIN AND DEVTOME PROJECTS ARE BOTH VERY IMPORTANT TO THE COMMUNITY. PLEASE CONTRIBUTE TO ITS FURTHER SUCCESS FOR ANOTHER 5 OR MORE YEARS!

Programming Microsoft Word - 10 - Working With A Collection (Part 2)

What are we going to do in this tutorial?

In the previous tutorial, you finished a program that could take in data (of an Individual or Company, save that data in (or take data from) a Microsoft Access database and collect the details of various parties in a collection. Doing so, your program used classes, including a custom Visual Basic collection class.

In this part 2 of the tutorial, its time form the final step: getting that data written out in a Microsoft Word document. In order to do that, you will use code from this earlier tutorial. For your convenience, the steps are shown again below. This code needs input to write in the Word Document. That input resides in the collection class as you may remember from the previous tutorial. We are going to show you how to get all that data from the collection and insert it in a Word document.

Let's get started.

Step 1

First, we build a simple Word template and add code to open that template and insert text. Start Microsoft Word (this tutorial uses Word 2007).

01.jpg

Open a new document: Office button → New → (next window) Create

Type the following text. You can create your own text but make sure that you insert the marked text with brackets ”[…]” in the text as shown.

Save the document as a Word Template: Office button → Save As → Word Template → name the file 'Devcoin.Contract.dotx' and Save on the desktop (easier to find in the next steps)

Close the Word template.

Step 2

Start Visual Basic and open the program from the previous tutorial (MyFirstFormWithDatabase). You are going to expand on this program.

First, add the Word template just created to the project. In the Solution Explorer, right-click the program's name and select in the menu:

In the file menu, make sure that you set the file filter to 'All files'. Go to your desktop and select the file Devcoin.Contract.dotx'. Click 'Add' and the file is added to the project.

In the Solution Explorer, select the file and go to the Properties window. Make sure that the property 'Copy to Output Directory' is set to 'Copy if newer' or 'Copy always'.

The template is ready for use. Let's add the basic code for opening Word and inserting text.

Step 3

Add a new class to the project by right-clicking the Solution Explorer and using the menu: Add → Class.

Name the new class 'WordActions' and click 'Add'

The WordActions class opens in code view. Add a line on top of the class:

Imports Microsoft.Office.Interop

Also add a reference to the Word object model. In the top menu: Project → Add Reference

In the next window, search for 'word' as indicated, select the Word library and click OK (you must have Word or Office installed for this to work).

This will set up your program to interact with Word.

Step 4

Next steps will be to create code that will open a Word document. In an earlier tutorial, code was suggested to properly open a Word document. This code caused some issues in a subsequent tutorial, in which a quick and dirty solution was used (basically, using code twice with small variations). Let's do this better in this tutorial.

For a good understanding, recall that the program mostly interacts with Microsoft Word through elements of the Word.Application object and the Word.Document object. Ideally, we want to have both objects available (and attached to a running instance of Word) so the code can either address the Word.Document object or the Word.Application object; whatever is needed for the specific code there.

As a basic principle, code should be written in such manner that each block of code performs a task fully without requiring other code to contain bits and pieces of that task to make it work. Otherwise, code will be difficult to reuse and understand.

The previously routine used to open a Word document causes an issue. The code contains a function (called 'OpenWordTemplate' in those tutorials) that only returns one object. In the tutorials used this had to be either the Word.Document object or the Word.Application object. The code to insert text in a Word bookmark required a Word.Document object to work with, and the code to replace text (Find and Replace) in a document needed a Word.Application object. In the earlier tutorial, this was solved by using two versions of the OpenWordTemplate function (one returning a Word.Document object and the other returning a Word.Application). This is bad practice, because heaps of code are written twice.

Let's avoid this problem by enhancing the code to open the Word document. You will build a function that does what it needs to do: open a Word document on the basis of the Word template in our project. But at the same time, the function will return 2 objects: a Word.Document object and a Word.Application. This enables you to bring those objects into the main code block that contains or calls the entire set of instructions to open a Word document and populate it. It will become clear when writing the code.

First, you need to create a so-called 'Structure'. A structure is an easy method to build an object that can hold various types of data or objects. In the WordActions class, insert the following code (place it just below the 'Public Class WordActions' line):

Public Structure wrdObjects
        Dim wordDoc As Word.Document
        Dim wordApp As Word.Application
    End Structure

Looking at the code, the collection of objects will be named 'wrdObjects' and its elements are 'wordDoc' and 'wordApp' as Word.Document and Word.Application objects, respectively.

The Function to open a Word document will use this structure to store both the Word.Application object and Word.Document object used in that function. Let's build the function and analyze it. Insert the following code below the code for the Structure you just inserted in the WordActions class.

Public Shared Function OpenWordTemplate(doc As String) As wrdObjects

        Dim myWordObjects As New wrdObjects

        'Procedure OpenWordTemplate()
        '
        '1. check whether Word is running
        '2. if so, create a new document based on the supplied doc variable (a Word template)
        '3. if not, start word and then create a new document based on the supplied doc variable (a Word template)
        '
        '======================================
        'Code to check whether Word is running
        'courtesy of Cindy Meister, VSTO/Word MVP

        Dim oWord As Word.Application
        Dim oDoc As Word.Document
        Dim appName As String = "Word.Application"
        Dim wdProcesses() As System.Diagnostics.Process = _
          System.Diagnostics.Process.GetProcessesByName("winword")
        Dim wdprocess As System.Diagnostics.Process

        For Each wdprocess In wdProcesses
            System.Diagnostics.Debug.Print(wdprocess.MainWindowTitle)
            System.Diagnostics.Debug.Print(wdprocess.StartInfo.Arguments.Length.ToString())
        Next

        'If Word is running, attach oWord to that instance of Word
        If wdProcesses.Length > 0 Then
            oWord = System.Runtime.InteropServices.Marshal.GetActiveObject(appName)
            'if Word is not running, start new instance of Word
        Else
            oWord = New Word.Application
        End If
        If Not oWord Is Nothing Then
            oWord.Visible = True
            oWord.Activate()
        End If
        oWord.Visible = True
        '======================================

        'open new Word document based on the basic template
        Dim path As String = Application.StartupPath & "\"
        doc = path & doc
        oDoc = oWord.Documents.Add(doc)

        myWordObjects.wordApp = oWord
        myWordObjects.wordDoc = oDoc

        Return myWordObjects


        oDoc = Nothing
        oWord = Nothing

    End Function

Below, we analyze parts of this code.

Looking at the code, the first line is already interesting to note. Look at the following code:

Public Shared Function OpenWordTemplate(doc As String) As wrdObjects

This tells us that the Function 'OpenWordTemplate' will take a 'doc' as string. The 'doc' is the name of the template to be opened as a new Word document. Second, the Function also states that it will return 'wrdObjects'. This is the structure just created. This means that the Function will return the object 'Structure' which will hold our wordDoc and wordApp objects.

Let's look at the next interesting block of code:

Dim myWordObjects As New wrdObjects

myWordObjects is declared as a new instance of the wrdObjects structure just created. This makes the structure available for use in this function.

        'open new Word document based on the basic template
        Dim path As String = Application.StartupPath & "\"
        doc = path & doc
        oDoc = oWord.Documents.Add(doc)
</pre >

To ensure that the program knows where to find the template to be opened, code is added to retrieve the location of the program, store this path in the variable 'path' and provide the Documents.Add command with a full path to the Word template to be used.

Now to the core changes to this function:

<pre>
myWordObjects.wordApp = oWord
myWordObjects.wordDoc = oDoc

Return myWordObjects

As stated above, the myWordObjects object is an instance of the structure wrdObjects. First, the structure is loaded with the objects oWord and oDoc. Next, the structure is 'returned', meaning that the structure 'myWordObjects' (including its elements just loaded) is passed by the function to the code that calls the function. Next step is calling this function from the main form.

Double click on the Form1.vb file in the Solution Explorer.

The design view of this form is shown.

Double click on the 'OK' button: you are brought to the code of this button. Delete the code between ' Private Sub btnOK_Click(sender ….. End Sub'.

Insert the following block of code:

Dim myWordObjects As WordActions.wrdObjects

        myWordObjects = WordActions.OpenWordTemplate("Devcoin.Contract.dotx")

        Dim oWord As Word.Application = myWordObjects.wordApp
        Dim oDoc As Word.Document = myWordObjects.wordDoc

Now test the program by running it and pressing the 'OK' button. Word opens a document on the basis of the Devcoin contract template. Note that the code first creates an instance of the wrdObjects structure, then proceeds to call the OpenWordTemplate function and subsequently declares objects oWord and oDoc and loads them with the objects wordApp and wordDoc that the function OpenWordTemplate returned. The oWord and oDoc objects are now ready for use in the rest of the code for this button.

Before finalising this code, the program needs some additional functionality. Let's code that in the next step.

Step 5

Your program will insert text in the Word document opened when clicking the 'OK' button (the Devtome contract). This can be done by taking the data in the MyPartiesCollection class (residing in the Form1 code) and cycling through this with a loop. However, the program needs to write out the party details for the Devtome contract (the Word document). Essentially, the program must take the data from each party class, stored in the MyPartiesCollection class, and build a proper sentence first. Subsequently, this sentence can be written in the Devtome contract. Open the code of the Party Class by double-clicking on 'Party.vb' in the Solution Explorer.

Insert the following code below the 'Public Class PartyIndividual … End Class'

Shared Function ReturnPartyline(ByRef party As Object) As String

        Dim partystring As String = Nothing
        Dim partycomp As PartyCompany
        Dim partyind As PartyIndividual


        Select Case TypeName(party)

            Case "PartyCompany"
                partycomp = party
                partystring = partycomp.Name & ", a company with corporate seat in " & partycomp.Seat & ", having its place of business at " & _
                    partycomp.Address & ", " & partycomp.City & ", " & partycomp.Country
            Case "PartyIndividual"
                partyind = party
                partystring = partyind.Name & ", born in " & partyind.PlaceOfBirth & ", having its place of business at " & _
                    partyind.Address & ", " & partyind.City & ", " & partyind.Country

        End Select

        Return partystring

    End Function

This function takes a 'party' as input (holding all relevant data of a party) and returns a sentence as a string. It is merely writing out a sentence, filling blanks with information contained in the elements of the 'party' object. This routine uses a 'Select Case' statement to make a distinction between a 'party' holding data of a Company and a 'party' holding data of an Individual. An 'If .. End If' statement would work as well, but is less efficient (the Select Case statement goes directly to the appropriate case instead of testing them all).

Next in line is coding a function that inserts the sentence in the Word document. A proper place for this function is the WordActions class. Double click the WordActions.vb file in the Solution Explorer and insert the following code as new function.

Public Shared Sub InsertParties(ByVal collection As PartiesCollection, ByRef oword As Word.Application)

        Dim partyline As String = Nothing
        Dim i As Integer
        Dim obj As Object
        Dim count As Integer = collection.Count
        Dim Sel As Word.Selection = oword.Selection


        For i = 1 To count

            obj = collection.Item(i)
            partyline = Party.ReturnPartyline(obj)


            If i = 1 Then



                Dim FindObject As Word.Find = oword.Application.Selection.Find
                With FindObject
                    .ClearFormatting()
                    .Text = "[parties]"
                    .Replacement.ClearFormatting()
                    .Replacement.Text = partyline
                    .Execute(Replace:=Word.WdReplace.wdReplaceAll)
                End With

                Dim rng As Word.Range = oword.ActiveDocument.Range
                rng.Find.ClearFormatting()

                If rng.Find.Execute(partyline) Then

                    Dim direction As Object = Word.WdCollapseDirection.wdCollapseEnd
                    rng.Select()
                    rng.Collapse(direction)
                    rng.Select()

                    If count = 1 Then

                        TypeText(".", Sel)

                    ElseIf count > 1 Then

                        TypeText(";" & vbCrLf, Sel)

                    End If
                Else
                    'do nothing
                End If


            ElseIf i > 1 And i < count Then

                TypeText(partyline & ";" & vbCrLf, Sel)

            ElseIf i = count Then

                TypeText(partyline & ".", Sel)

            End If


        Next i

    End Sub

Below, also insert the following code as a new Sub:

Public Shared Sub TypeText(ByVal text As String, ByRef Sel As Word.Selection)
        Sel.Text = text
        'the next line places the cursor at the end of the inserted text
        Sel.Start = Sel.End

    End Sub

You have probably notice that there are now three separate blocks of code (2 functions and 1 sub) to insert data from the 'party' object into the Word document. In the longer run, this makes your code reusable and better to manage. Let's analyze these latest two blocks of code some further, starting with the InsertParties function.

Public Shared Sub InsertParties(ByVal collection As PartiesCollection, ByRef oword As Word.Application)

The function's headline indicates that the functions takes in a collection from the PartiesCollection. Remember, this collection holds all 'parties' that were added to this collection (both Companies as Individuals; depending on what the user chose). Furthermore, an existing Word.Application is fed into the function 'by reference'. This enables the function to work with the Word instance that was created when opening the Word document. You have not seen code yet that actually opens the appropriate Word document. Don't worry, that will follow.

Dim partyline As String = Nothing
        Dim i As Integer
        Dim obj As Object
        Dim count As Integer = collection.Count
        Dim Sel As Word.Selection = oword.Selection

Next are a couple of variables. Notice the 'count' and the 'Sel' variable. Essentially, the code needs to take the 'collection' object fed into the function, cycle through each element of that collection (each a 'party' object), return a sentence based on each party's details and place that sentence in the Word document.

The cycle part of the code is shown below in pieces.

For i = 1 To count

            obj = collection.Item(i)
            partyline = Party.ReturnPartyline(obj)

The cycle is started with the familiar 'for .. to' routine. You do not have to adjust the 'count' here, because a collection in Visual Basic starts at 1. Next, the 'party' object that belongs to the count number is retrieved from the collection with 'collection.Item(i)'. This is a function that is part of the collection class. Second, the function 'ReturnPartyLine' is invoked, feeding it with the object just retrieved (a party). The sentence to be inserted is thus assigned to 'partyline' as a string.

All very compact code because the code is elsewhere.

Moving on with analyzing the code.

            If i = 1 Then



                Dim FindObject As Word.Find = oword.Application.Selection.Find
                With FindObject
                    .ClearFormatting()
                    .Text = "[parties]"
                    .Replacement.ClearFormatting()
                    .Replacement.Text = partyline
                    .Execute(Replace:=Word.WdReplace.wdReplaceAll)
                End With

The first party is inserted in the document at the tag [parties] (in the process replacing this tag in the document). This is done by the find.. replace routine. Notice that the code must first meet the test that count = 1 so that indeed it concerns the first party to be inserted. Note: the find .. replace routine requires a Word.Application object. That's why the 'oword' As Word.Application is fed into the function.

Moving on. The code has now inserted the details of the first party in the Word document. The code must now investigate whether there will be a second party inserted in the Word document (remember, the code cycles through all 'parties' contained in the collection. The following code does exactly so.

                Dim rng As Word.Range = oword.ActiveDocument.Range
                rng.Find.ClearFormatting()

                If rng.Find.Execute(partyline) Then

                    Dim direction As Object = Word.WdCollapseDirection.wdCollapseEnd
                    rng.Select()
                    rng.Collapse(direction)
                    rng.Select()

                    If count = 1 Then

                        TypeText(".", Sel)

                    ElseIf count > 1 Then

                        TypeText(";" & vbCrLf, Sel)

                    End If
                Else
                    'do nothing
                End If

The code first sets the cursor in the Word document at the very end of the first party details just written in the document. This is necessary because the find..replace routine does not leave the cursor at the end of the text just inserted (the cursor will remain at the default position when opening the Word Document, being the very beginning of the Word document).

For this, a range object is needed and some code to make it work. Essentially, the code looks for the party details just inserted, selects it, collapses the selection to the very end (after the last word of the party details just inserted) and places the cursor there.

Subsequently, the code tests whether there will be only one party inserted (count is 1 then) or more. In case of one party, the code inserts a dot after the party details just inserted. In case of more parties, the code inserts a semi-column and give an enter, ready to insert the next party details.

The rest of the code deals with inserting the second and possibly later parties.

            ElseIf i > 1 And i < count Then

                TypeText(partyline & ";" & vbCrLf, Sel)

            ElseIf i = count Then

                TypeText(partyline & ".", Sel)

            End If


        Next i

There are 2 tests: whether 'i' in the loop is more than one but not the last number or whether 'i' is the last item. In case of the former, the party details are inserted with a semi-column, followed by a enter. In case of the latter, the party details are inserted followed by a dot.

The code to insert text on these two occasions is the regular 'typetext' command, available as part of the Word object model. This command requires a 'selection', also a regular element of the Word object model. The 'TypeText' sub below deals with this.

Public Shared Sub TypeText(ByVal text As String, ByRef Sel As Word.Selection)
        Sel.Text = text
        'the next line places the cursor at the end of the inserted text
        Sel.Start = Sel.End

    End Sub

The sub takes in a text as string and a selection (in the Word document). The sub places the text string in the Word document at the place of the selection.

This concludes the analysis of the code. One final step: actually issuing the proper command in the main form to set this whole barrage of code into motion!

Step 6

Double-click on 'Form1.vb' in the Solution Explorer. In the design view of Form1, double-click the 'OK' button. The code for the 'OK' button on Form1 will be shown.

Insert the following code between 'Private Sub btnOK_Click …. End Sub':

Dim myWordObjects As WordActions.wrdObjects

        myWordObjects = WordActions.OpenWordTemplate("Devcoin.Contract.dotx")

        Dim oWord As Word.Application = myWordObjects.wordApp
        Dim oDoc As Word.Document = myWordObjects.wordDoc

        'code to insert text at a bookmark in the opened Word template
        'Find and replace tag [name] with input from textbox1

        WordActions.InsertParties(MyPartiesCollection, oWord)

First, a new Word document is opened on the basis of our Word template. As part of that process, the objects Word.Application and Word.Document are brought into the code on Form1 (contained in a structure called 'myWordObjects'). Next, these objects are attached to variables 'oWord' and 'oDoc'. This makes the code ready to work with a Word document.

The final step is setting the rest of all code in motion. One single line is sufficient to start and finish everything. That's proper coding…. The command 'InsertParties' does just what the name says: inserting all relevant data in the Word document.

Conclusion

In this tutorial, you have build code to insert parties in a Word document. The code is flexible; it can handle inserting one party or a countless number of parties. The code is spread logically over the project so it is easily maintainable and reusable. Great work!

In the next tutorial, you are going to enhance the operation of the database some further and assist the user with selecting the right data to be inserted in the Word document.

Computing A-Z | Programming | Software


QR Code
QR Code programming_microsoft_word_-_10_-_working_with_a_collection_part_2 (generated for current page)
 

Advertise with Anonymous Ads