none
VB.NET Dynamic Form RRS feed

  • Question

  • Hello,

    Currently I am attempting to create a VB.NET program that will dynamically generate a form from a Word template. Specifically, the user interface would allow me to select a template document (the list is populated from a directory containing Word templates), and from the template it would read in some fields/variables that the document contains and generate a Windows Form. The Windows Form would contain text boxes that can be filled in, and then the document (the template with the filled in fields) could be generated and saved.

    I've been running into trouble with what to use within the Word template. I've tried Form Fields and DocVariables so far, wondering if there is a good way to go about it? My issue with the form fields is that they are dependent on bookmarks, so they cannot repeat as bookmarks are unique (so each field must be entered individually instead of repeating fields getting filled after one entry is made). My issue with DocVariables is that there isn't a way to get the name of the DocVariables and create a master list of them to generate the form from.

    Am I going about this incorrectly? This type of project must have been done before. I essentially just want to have a user upload a template to a directory and run an application that will generate a form that can fill in things in the document. I'd like to avoid automating find and replace (not sure if it's good performance-wise). I want to be able to add templates without having to code (hence the desire for a form that is created at runtime).

    Tuesday, June 18, 2019 7:05 PM

Answers

  • I'm going to guess that what you may be doing is inserting { DOCVARIABLE } fields (e.g. { DOCVARIABLE Test } ) in your template, but not creating the related variables. In that case, the result of the updating the DOCVARIABLE fields will be some error messages.

    Since there's no built-in UI in Word to create Document Variables you basically have to create them using the object model (e.g. when testing I generally just use the VBE Immediate window to issue something like 

    ActiveDocument.Variables("Test").Value = "some default value"

    (that either modifies the value of an existing Variable or creates the Variable if it does not exist).

    If I'm right about that, what to do depends on who's creating these documents. If it's end users, then just inserting DOCVARIABLE fields isn't going to be enough (and might be a little confusing for them). If it's the developer (e.g. you) then it really is just a question of creating the Variables as well as the fields. Either way, I guess you could consider creating a bit of UI that would help you insert a DOCVARIABLE field at the insertion point *and* create the Variable.

    However, if you basically just want to start with the DOCVARIABLE fields, another approach would be to iterate the collection of fields in the document, looking for DOCVARIABLE fields, and retrieve the variable names from the .code.text of the field. To be thorough, you'd have to iterate through all the different StoryRanges. 

    Which is sort of why I still think the content control/custom XML part approach is actually better, but I get that you want to stick to an existing pattern.

    The other possibility that springs to mind is that the variables exist in the templates but are not being created in documents based on them for some reason. Because if they are in the template, documents created from that Template should also have them.

    Peter Jamieson

    • Marked as answer by Matt_946 Thursday, June 20, 2019 5:12 PM
    Wednesday, June 19, 2019 4:59 PM

All replies

  • 1. You should be able to iterate through the Document.Variables collection to retrieve the names of all the Document Variables in the template. You should use a naming convention to distinguish between variables who values you want to collect on the form, and variables that are there for other purposes. 

    If there is some reason that cannot work please state it as it may help clarify the problem you are facing.

    2. However, a far better route is probably to use a custom xml part to store the data, and possibly the variable names and any other data you might want to use on the form (e.g. tiptexts etc.), and use content controls mapped to a custom xml part to insert the data in the document. This is, in fact, one of the main reasons for having content controls+custom xml parts. There are many possible ways you could make this work, but at its simplest, you could
     a. create a single custom xml part that contains one xml element for each value you want to collect. Because you may have other XML parts, and content controls that have nothing to do with the form, you could use the namespace URI of the XML document to tell you that this is the part you need to work with.
     b. you can get the part using automation, but if you do not actually need to open the document using Word at this stage, you could use the Office Open XML SDK to retrieve and replace the XML part. That would allow your application to work even on systems without Word, and especially on servers. Either way, you could then iterate through the part's elements to get the names of the data items for your form.
     c. Use plain text content controls, checkbox content controls and/or date picker controls in the document, and map them to the Custom XML Part. This can be done using VBA etc., or manually using the XML mapping pane in the Developer tab in Word.

    The overall process would then look something like:
     a. select the template
     b. open it using automation or the SDK and identify the correct part
     c. retrieve the XML document from the part
     d. use the element names in the XML to construct your form
     e. display the form and collect the values
     f. insert the collected values into the XML document
     g. write the XML part to the document

    With this approach, you do not need to do anything else to the document - when a user next opens the document, Word will update the content controls with the values in the XML part.

    If you want to separate metadata (keep data item names, descriptions, tooltip info., validation data, etc.), you could have one or more extra XML parts to do that. This is in effect how SharePoint works with property data in Word documents. You could for example store an XML Schema for your data in a separate part. 

    Peter Jamieson

    Wednesday, June 19, 2019 6:58 AM
  • Thank you for your help Peter. I was considering using XML but I want the application to look similar to one that was already developed for later integration (the other tool uses Document Variables, but it does not do what I'd like to do with this tool, which is create forms dynamically from them).

    The trouble I run into with iterating through the Document.Variables collection is that the collection is empty. I have to reference the variables by key to set their values.

    Dim KeysToAdd As ArrayList = New ArrayList From {
        "Test",
        "Test2"
    }
    For Each key In KeysToAdd
        objDoc.Variables(key).Value = "Test worked"
        MessageBox.Show("Variable Found: " & 
                         objDoc.Variables(key).Name)
    Next

    Then, I update the values of the document variables, and any document variable that has a key in the objDoc.Variables collection has it's value updated. Ideally I would like to be able to get a list of the keys from the document to create the KeysToAdd arraylist so the program can be dynamic.

    Wednesday, June 19, 2019 12:53 PM
  • You should be able to iterate the Variables like this (I just checked this in VB.NET):

    Dim objVar As Microsoft.Office.Interop.Word.Variable
    For Each objVar In objDoc.Variables
      '  ( I am testing using a Console App).
      Console.WriteLine(objVar.Name & ": " & objVar.Value)
    Next
    
    or like this:

    Dim i As Integer
    For i = 1 To objDoc.Variables.Count
      Console.WriteLine(objDoc.Variables(i).Name & ": " & objDoc.Variables(i).Value)
    Next
    

    Does neither of those work for you? (NB, if you need to insert into or delete from a collection like this it's sometimes better to count down from .Count to 1).



    Peter Jamieson

    Wednesday, June 19, 2019 2:22 PM
  • The issue is that objDoc.Variables is empty, so .Count returns 0. I think I am possibly inserting the variables incorrectly into Word? If you've succeeded in running this loop without previously referring to the objects in the objDoc.Variables collection explicity, how did you add Document Variables to your Word doc?
    Wednesday, June 19, 2019 4:39 PM
  • I'm going to guess that what you may be doing is inserting { DOCVARIABLE } fields (e.g. { DOCVARIABLE Test } ) in your template, but not creating the related variables. In that case, the result of the updating the DOCVARIABLE fields will be some error messages.

    Since there's no built-in UI in Word to create Document Variables you basically have to create them using the object model (e.g. when testing I generally just use the VBE Immediate window to issue something like 

    ActiveDocument.Variables("Test").Value = "some default value"

    (that either modifies the value of an existing Variable or creates the Variable if it does not exist).

    If I'm right about that, what to do depends on who's creating these documents. If it's end users, then just inserting DOCVARIABLE fields isn't going to be enough (and might be a little confusing for them). If it's the developer (e.g. you) then it really is just a question of creating the Variables as well as the fields. Either way, I guess you could consider creating a bit of UI that would help you insert a DOCVARIABLE field at the insertion point *and* create the Variable.

    However, if you basically just want to start with the DOCVARIABLE fields, another approach would be to iterate the collection of fields in the document, looking for DOCVARIABLE fields, and retrieve the variable names from the .code.text of the field. To be thorough, you'd have to iterate through all the different StoryRanges. 

    Which is sort of why I still think the content control/custom XML part approach is actually better, but I get that you want to stick to an existing pattern.

    The other possibility that springs to mind is that the variables exist in the templates but are not being created in documents based on them for some reason. Because if they are in the template, documents created from that Template should also have them.

    Peter Jamieson

    • Marked as answer by Matt_946 Thursday, June 20, 2019 5:12 PM
    Wednesday, June 19, 2019 4:59 PM
  • That's the distinction that I needed clarification on. .code.text was what I was looking for, thanks Peter. I would use custom XML but the issue is that I won't be creating templates, someone else will be. My goal is really to make sure that templates can be created all within Word, then added to a directory that the application can then pull from. If I used custom XML I'm assuming that the templates would be a little more complex to make?
    Thursday, June 20, 2019 5:15 PM
  •  If I used custom XML I'm assuming that the templates would be a little more complex to make?

    Yes, although I think you could probably get the users to create CCs and use a naming convention (the CC name and/or tag), it would involve more steps, and you'd have to give them access to the relevant cc insertion and property functions or provide your own UI. So harder.

    If your users have SharePoint, things *might* be a little different as you can create a SharePoint folder, create some properties in that folder, and SharePoint will then create the appropriate Custom XML Part, populate the Elements and Word will also generate insertion options for content controls linked to those Elements under Insert->Document Property. There's more, but that's really another story anyway.

    Peter Jamieson

    Thursday, June 20, 2019 6:31 PM