Link to home
Start Free TrialLog in
Avatar of acdagirl
acdagirl

asked on

prefilling word bookmarks through vb.net - need to preserve bookmarks

I have the following vb.net code to populate bookmarks in a word doc. My problem is that I want to preserve the bookmarks following the text input.
I pretty much list the bookmark names and values I wish to import into the doc in an array and push the values in. I believe that because I'm not matching the actual bookmarks to the *range* (only the bookmark name), I can't reset the bookmark properly. The prefilling of the qualifier does work though...

here is the sub:
 Protected Sub prefillDoc(ByVal docName As String)
        Try
            Dim bookmarkArray As New ArrayList
            Dim bookmarkArrayValue As New ArrayList

            Dim file_title As String = docName.Substring(docName.LastIndexOf("\") + 1)
            Dim file_path As String = docName.Substring(0, docName.LastIndexOf("\"))

            Dim word_server As New Word.Application
            word_server.Visible = True

            word_server.ChangeFileOpenDirectory(file_path)
            word_server.Documents.Open(FileName:=file_title)

            If word_server.Selection.FormFields.Application.ActiveDocument.ProtectionType = Word.WdProtectionType.wdAllowOnlyFormFields Then
                word_server.Selection.FormFields.Application.ActiveDocument.Unprotect()

                bookmarkArray = getBookmark() ' NOTE: this gets 2-D array from another sub
                For Each bookmarkName As String In bookmarkArray
                    Dim strbookmarkName As String() = bookmarkName.Split(";")
                    Dim bmname As String = strbookmarkName(LBound(strbookmarkName))
                    Dim bmvalue As String = strbookmarkName(UBound(strbookmarkName))

                    word_server.Selection.GoTo(What:=Word.WdGoToItem.wdGoToBookmark, Name:=bmname)
                    'word_server.Selection.TypeText(Text:=bmvalue)

'here's where it all goes wrong
                    Dim range As Word.Range = word_server.Selection.FormFields.Application.ActiveDocument.Bookmarks(bmname).Range()
                    range.Text = bmvalue
                    word_server.ActiveDocument.Bookmarks.Add(bmname, range)
'end of wrong
                    word_server.Visible = True
                Next
                word_server.Selection.FormFields.Application.ActiveDocument.Protect(Word.WdProtectionType.wdAllowOnlyFormFields)
                word_server.Documents.Save()
                'word_server.Documents.Close(SaveChanges:=True)
                word_server.Application.Quit()
                word_server.Quit()
                word_server = Nothing
            Else
                word_server.Application.Quit()
                word_server.Quit()
                'word_server.Documents.Close(SaveChanges:=False)
                word_server = Nothing
                Throw New ApplicationException("Error accessing protected qualifier document: " & qualifierName)
            End If
        Catch comex As COMException
            'word_server.Application.Quit(False)
            'word_server.Quit()
            'word_server = Nothing
            Throw comex
        Catch ex As Exception
            'word_server.Application.Quit(False)
            'word_server.Quit()
            'word_server = Nothing
            Throw ex
        End Try
    End Sub
Avatar of GrahamSkan
GrahamSkan
Flag of United Kingdom of Great Britain and Northern Ireland image

I cannot see anything that you are doing that would definitely cause your code not to work, however I would advise:

Avoid the use of the Selection object and the GoTo method
Use a document object
Avoid giving object variables their class name (Range)

You have:
                   word_server.Selection.GoTo(What:=Word.WdGoToItem.wdGoToBookmark, Name:=bmname)
                    'word_server.Selection.TypeText(Text:=bmvalue)

'here's where it all goes wrong
                    Dim range As Word.Range = word_server.Selection.FormFields.Application.ActiveDocument.Bookmarks(bmname).Range()
                    range.Text = bmvalue
                    word_server.ActiveDocument.Bookmarks.Add(bmname, range)
'end of wrong

The way that I would do that (in VB6) is:

'declarations to show types
Dim word_server As Application
Dim wdDoc As Word.Document
Dim bmname As String

Set wdDoc = word_server.Selection.Document
                    'word_server.Selection.GoTo(What:=Word.WdGoToItem.wdGoToBookmark, Name:=bmname)
                    'word_server.Selection.TypeText(Text:=bmvalue)

'here's where it all goes wrong
                    Dim rng As Word.range
                    Set rng = wdDoc.Bookmarks(bmname).range
                    rng.Text = bmvalue
                    wdDoc.Bookmarks.Add bmname, rng
'end of wrong

Putting the executable code into .Net:

                    Dim rng As Word.range
                    rng = wdDoc.Bookmarks(bmname).range
                    rng.Text = bmvalue
                    wdDoc.Bookmarks.Add( bmname, rng)
'end of wrong
Avatar of acdagirl
acdagirl

ASKER

So to summarize, here's what you'd have...

'declarations to show types
Dim word_server As Application
Dim wdDoc As Word.Document
Dim bmname As String

Set wdDoc = word_server.Selection.Document -- I'm confused here
Dim rng As Word.range
rng = wdDoc.Bookmarks(bmname).range
rng.Text = bmvalue
wdDoc.Bookmarks.Add( bmname, rng)

can you elaborate on the selection.Document part?
I tried it and no matter what I do I can't add the bookmarks back in. there are no errors during run, but as it's running I can see the bookmarks don't get re-set and when I reopen the doc, the fields are filled in but no bookmarks,

What am I doing wrong?
Set wdDoc = word_server.Selection.Document tries to set the wdDoc variable to the document being worked on.
That way, we should be able to define the current range for the bookmark.
When the range's text is set to the new value, the new rng variable should then define a different range in the document, and this should be captured by the re-adding of the bookmark.
Where does the bookmark end up?

Try capturing (as in debug.print) the Start and End properties of the range before and after the addition of the text.
I have tried to emulate what you are doing using vb.net (So it's my second lesson in .Net)

Here is the result, which seems to work as required.

It needs a forms-protected test document with three bookmarks: bmone, bmtwo and bmthree.

The program save the result under a new name, e.g  MyFile.Doc becomes MyFileNew.doc

Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Button1 As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.Button1 = New System.Windows.Forms.Button()
        Me.SuspendLayout()
        '
        'Button1
        '
        Me.Button1.Location = New System.Drawing.Point(190, 53)
        Me.Button1.Name = "Button1"
        Me.Button1.Size = New System.Drawing.Size(85, 40)
        Me.Button1.TabIndex = 0
        Me.Button1.Text = "Button1"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 273)
        Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.Button1})
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub
    Protected Sub prefillDoc(ByVal docName As String)
        Try
            Dim bookmarkArray As New ArrayList()
            Dim bookmarkArrayValue As New ArrayList()
            Dim bookmarkName As String
            Dim getbookmark(1, 2) As String
            Dim file_title As String = docName.Substring(docName.LastIndexOf("\") + 1)
            Dim file_path As String = docName.Substring(0, docName.LastIndexOf("\"))
            Dim wdDoc As Word.DocumentClass
            Dim word_server As New Word.ApplicationClass()
            Dim i As Integer
            word_server.Visible = True

            word_server.ChangeFileOpenDirectory(file_path)
            wdDoc = word_server.Documents.Open(FileName:=file_title)

            If wdDoc.FormFields.Application.ActiveDocument.ProtectionType = Word.WdProtectionType.wdAllowOnlyFormFields Then
                wdDoc.FormFields.Application.ActiveDocument.Unprotect()
                getbookmark(0, 0) = "bmone"
                getbookmark(0, 1) = "bmtwo"
                getbookmark(0, 2) = "bmthree"
                getbookmark(1, 0) = "one"
                getbookmark(1, 1) = "two"
                getbookmark(1, 2) = "three"
                Dim bmname As String
                Dim bmvalue As String

                'bookmarkArray = getbookmark ' NOTE: this gets 2-D array from another sub
                'For Each bookmarkName As String In bookmarkArray
                For i = 0 To 2
                    'Dim strbookmarkName As String() = bookmarkName.Split(";")
                    bmname = getbookmark(0, i)
                    bmvalue = getbookmark(1, i)

                    'word_server.Selection.GoTo(What:=Word.WdGoToItem.wdGoToBookmark, Name:=bmname)
                    'word_server.Selection.TypeText(Text:=bmvalue)

                    'here's where it all goes wrong
                    Dim rng As Word.Range = wdDoc.Bookmarks.Item(bmname).Range()
                    rng.Text = bmvalue
                    wdDoc.Bookmarks.Add(bmname, rng)
                    'end of wrong
                    word_server.Visible = True
                Next
                wdDoc.Protect(Word.WdProtectionType.wdAllowOnlyFormFields)
                wdDoc.SaveAs(Replace(file_title, ".", "new."))
                wdDoc.Close()
                'word_server.Documents.Save()
                'word_server.Documents.Close(SaveChanges:=True)
                word_server.Quit()
                word_server = Nothing
            Else
                word_server.Quit()
                'word_server.Documents.Close(SaveChanges:=False)
                word_server = Nothing
                'Throw New ApplicationException("Error accessing protected qualifier document: " & qualifiername)
            End If
            'Catch comex As COMException
            'word_server.Application.Quit(False)
            'word_server.Quit()
            'word_server = Nothing
            'Throw comex
        Catch ex As Exception
            'word_server.Application.Quit(False)
            'word_server.Quit()
            'word_server = Nothing
            Throw ex
        End Try
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        prefillDoc("C:\Documents and Settings\Graham Skan\My Documents\bookmarktest.doc")
    End Sub
End Class
Thanks alot - I can only see a few differences:
you're using the DocumentClass and ApplicationClass - not sure why not just the interfaces
also you're changing the filename   wdDoc.SaveAs(Replace(file_title, ".", "new.")) -- I want to keep it the same, but that's a moot point - if it works like that, I can certainly work around it.

I'll give it a try and see what happens...
             
I used the extended 'Class' objects because I was getting 'ambiguous' -type error messages like:

C:\Documents and Settings\Graham Skan\My Documents\Visual Studio Projects\WindowsApplication16\Form1.vb(107): 'Close' is ambiguous across the inherited interfaces 'Word._Document' and 'Word.DocumentEvents2_Event'.

The save under a new name was done to ensure that the input document doesn't get modified.

I have also modified some things in the error handling. Some errors - which I didn't understand - were being set and preventing the code from completing.
oh right...

You removed the throw COM exception.. ok, I'll do the same and see what I get. Thanks again :-)
hmm, everything works the same as with my other code - the bookmarks aren't reattached. I must be doing something in the bookmark array or the way the bookmarks are set in the doc? because the code is fine...
can you add this method to your code and see if it still works:

      Protected Function getBookmark() As ArrayList
        Try
            Dim bookmarkArray As New ArrayList
            Dim bookmarkArrayValue As New ArrayList

            arraybmone = "bmone;one"
            bookmarkArray.Add(arraybmone )

        arraybmtwo = "bmtwo;two"
            bookmarkArray.Add(arraybmthree )

        arraybmthree = "bmthree;three"
            bookmarkArray.Add(arraybmthree )

return bookmarkArray

End Function

and then within the prefill function use this logic for the retrieving the bookmarks and values?
[.......]
               Dim bmname As String
                Dim bmvalue As String

                bookmarkArray = getbookmark
                For Each bookmarkName As String In bookmarkArray
                    Dim strbookmarkName As String() = bookmarkName.Split(";")
                    bmname = strbookmarkName(LBound(strbookmarkName))
                    bmvalue = strbookmarkName(UBound(strbookmarkName))
[........]

If your code still works, then obviously I'm doing something incorrect in the array or somewhere external to the prefill.
oh sorry - you'll need to add the declarations at the top of your page:

   Dim arraybmone As String = ""
   Dim arraybmtwo As String = ""
   Dim arraybmthree As String = ""
oh man! I just realized what's going on... unbelievable...
the bookmarks are being reattached as supposed to, so the code is correct...
my problem is that the bookmarks were originally input fields - not just bookmarks.

Is there any way to make these new bookmarks input fields again after prefilling them? I think that's what I wasn't getting correct...
Do you mean that they are TextInput formfields?
yes, exactly...
ASKER CERTIFIED SOLUTION
Avatar of GrahamSkan
GrahamSkan
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ok, i think it works - i got some bad parameter name error, but i think that has something to do with the form field name not being a bookmark name maybe. I'll check, but so far it looks alot better... thanks so much for your help!
Thanks. Just to note that if a FormField has a name, then it is the name of its bookmark.
ok thanks :-)