Making Game: Auto-updating a date throughout a doc from a table cell

Original Source Link

I have a Word document with a version table in it. The version table is simply a 3-column table with version number, date of editing, and summary of changes. There are a few points in the doc where the version number and last-edit date are used. I would like to have these be done automatically.

I have solved the version problem. I created a “hidden” top row of the table (no border, white letters) and used the formula =MAX(BELOW). Since the versions are increasing numbers, this works, and that cell is (invisibly) populated by the latest version number. I bookmarked that field as LatestDocVersion, added {REF LatestDocVersion} where I want that to automatically update, voilĂ ! All done.

The date is another matter. The goal is for the last entry of that column to be used elsewhere in the doc– when a new version is added to the table, the date should automatically update to the date of that version. I can’t use the same method, since MAX doesn’t appear to understand dates and returns 0. I’ve tried to find a way to reference the last cell in the column, (something like {=R&(COUNT(BELOW)-1)&C2} except, you know, actually functional), but that hasn’t worked, and when it’s hard-coded for a specific cell it only returns a 0.

Is there a way to do this? I’m quite new to formulas and bookmarks, so I’m likely missing something completely obvious.

Because, as you say, the { = } field feature only works with numeric values, it’s difficult to come up with a scheme using fields and/or bookmarks that would actually be easy to use.

Probably the simplest approach would be the rather obvious one of inserting the new “current version date” using a couple of field codes, like this:

{ SET lastedit "2020-05-29" }{ lastedit }

(And use { lastedit } elsewhere in your document where you need that date.)

For that to work, you would always need to go back to the previous version’s date and unlink the two corresponding fields before inserting the new date.

You could avoid that by using a slightly more complex set of fields, like this:

{ SET "edit{ SEQ v }" "2020-05-30" }{ "edit{ SEQ v c }" }{ SET lastedit { "edit{ SEQ v c }" }

{ SEQ v } inserts the next number in a sequence called “v”. { SEQ v c } inserts the current value of the sequence called v. So each date in your table is set to a different bookmark name, edit1, edit2 etc.

e.g. for the third data row in the table, that looks like

{ SET edit3 "2020-05-30" }{ edit3 }{ SET lastedit { edit3 } }

If you have an autotext or some such that lets you insert that set of fields, all you need do is insert them in the final table row, edit the date, then re-execute all the fields.

A possible advantage is that you could also reference older version dates.

You might be able to simplify that. If it’s just for you, it’s obviously workable. But it’s a judgment call whether it’s viable beyond that.

There is completely different approach that you could consider that uses mapped Content Controls, and a Repeating Group content control. You have to use VBA to set it up, and for it to work properly (i.e. where inserting a new date automatically results in instantaneous changes elsewhere in the document) you need VBA that will respond to a particular Event.

But even without VBA, all that is needed to make the relevant dates update is for the user to save, close and re-open the document.

Here is some set-up code if you want to try it. You would start with a completely empty document, run the code (I leave you to find out how to install and run it).

The code creates a “Custom XML Part” to store data in the document. It then creates a 2-row, 3-column table.

It puts a text content control in each cell in row two and “maps” those controls to the Custom XML Part. What that means is that any Content Control that is also mapped to that item in the data store will have its value updated automatically by Word.

It then wraps row 2 in a Repeating Group Content control that is also mapped to the Part. That means that when you create a new row in the table (select the row and click the “+” box that appears to the right), you get a new row in the table and a new element corresponding to that row in the Part.

Finally, the code creates a Content control for the latest version number and the latest edit date. These are mapped to the last appropriate element in the Part. You would just need to copy these and paste copies elsewhere in the document to have them reflect the values in the table.

If you start by entering text in row 2, columns 1 and 2 you should see the value displayed immediately in any copies of the relevant content controls. If you change the values in the table, the other values should change. I have locked the non-table Content Controls for editing so you can’t wreck the table by typing values into them.

If you now create a new row in the table using that “+” feature, and enter version and date values, the values of the non-table Content Controls do not automatically update. It’s because Microsoft has chosen to limit the type of change it detects in the Custom XML Part. (Not unreasonably IMO). But if you now save, close, and re-open the document you should see that the non-table Content Control values have now updated. As I said earlier, it would be possible to make that happen immediately with a small piece of VBA, but is it really necessary?

The code:

Sub recreateCXPandMapCCs()
' Specify a namespace for your CXP
Const myns As String = "vt1"
' Specify the name of the Bookmark that
' marks the Version Table
Const VTBmName As String = "VersionTable"
Dim bm As Word.Bookmark
Dim cc As Word.ContentControl
Dim ccs As Word.ContentControls
Dim cxp As Office.CustomXMLPart
Dim cxps As Office.CustomXMLParts
Dim doc As Word.Document
Dim i As Long
Dim rng As Word.Range
Dim s As String
Dim tbl As Word.Table

' Build the initial XML for our Custom XML Part
' You can use the names that you prefer.
s = ""
s = s & "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
s = s & "<versionTable xmlns='" & myns & "'>" & vbCrLf
s = s & "  <theTable>" & vbCrLf
s = s & "    <theRow>" & vbCrLf
s = s & "      <versionNumber/>" & vbCrLf
s = s & "      <editDate/>" & vbCrLf
s = s & "      <summary/>" & vbCrLf
s = s & "    </theRow>" & vbCrLf
s = s & "  </theTable>" & vbCrLf
s = s & "</versionTable>"

' Point to the document we want to process
Set doc = ActiveDocument

With doc

  ' select and delete any existing CXPs with the specified namespace
  Set cxps = .CustomXMLParts.SelectByNamespace(myns)
  For Each cxp In cxps
  Set cxps = Nothing
  ' Create a new CXP
  Set cxp = .CustomXMLParts.Add(s)

  ' Delete and recreate our versioning table, and insert
  ' which we have bookmarked usin the name "VersionTable"
  ' If the bookmark doesn't exist, create a new table
  ' at the end of the document

  Set rng = Nothing
  For Each bm In .Bookmarks
    If bm.Name = VTBmName Then
      Set rng = bm.Range
      rng.Text = ""
      Exit For
    End If
  If (rng Is Nothing) Then ' bookmark not found
    Set rng = .Range(.Range.End - 1, .Range.End - 1)
  End If

  With rng
    Set tbl = .Tables.Add(Range:=rng, numrows:=2, numcolumns:=3)
    With tbl
      .Range.Bookmarks.Add VTBmName
      .Cell(1, 1).Range.Text = "Version"
      .Cell(1, 2).Range.Text = "Date"
      .Cell(1, 3).Range.Text = "Summary"
      Set cc = .Cell(2, 1).Range.ContentControls.Add(wdContentControlText)
      With cc
        .Title = "Version"
        .XMLMapping.SetMapping "//ns0:versionNumber[1]", , cxp
      End With
      Set cc = .Cell(2, 2).Range.ContentControls.Add(wdContentControlText)
      With cc
        .Title = "Date"
        .XMLMapping.SetMapping "//ns0:editDate[1]", , cxp
      End With
      Set cc = .Cell(2, 3).Range.ContentControls.Add(wdContentControlText)
      With cc
        .Title = "Summary"
        .XMLMapping.SetMapping "//ns0:summary[1]", , cxp
      End With
      Set cc = .Rows(2).Range.ContentControls.Add(wdContentControlRepeatingSection)
      With cc
        .AllowInsertDeleteSection = True
        .RepeatingSectionItemTitle = "Version Info"
        .XMLMapping.SetMapping "//ns0:theRow[1]", , cxp
      End With
    End With
    Set tbl = Nothing

    ' Now add "CurrentVersion" and "VersionDate" ccs at the end of the document
    .Start = doc.Range.End - 1
    .End = doc.Range.End - 1
    Set cc = .ContentControls.Add(wdContentControlText)
    With cc
      .Title = "LatestVersion"
      ' the key to all this is "last()" 
      .XMLMapping.SetMapping "/ns0:versionTable[1]/ns0:theTable[1]/ns0:theRow[last()]/ns0:versionNumber[1]", , cxp
      ' Ensure the user cannot type into this control
      .LockContents = True
    End With
    .Collapse wdCollapseEnd
    Set cc = .ContentControls.Add(wdContentControlText)
    With cc
      .Title = "LastEdited"
      .XMLMapping.SetMapping "/ns0:versionTable[1]/ns0:theTable[1]/ns0:theRow[last()]/ns0:editDate[1]", , cxp
      .LockContents = True
    End With
    Set cc = Nothing
  End With
  Set rng = Nothing
  Set cxp = Nothing
End With
Set doc = Nothing

End Sub

You could create a character style that is only to be applied to the date cells in your table. Then you can just use a STYLEREF field elsewhere in your document to look for that new style (which will always be at the bottom of the table) so STYLEREF fields after that point will always find that last date. Because you apply the character style to the cell, when you create a new row, that character style will automatically carry down to the new row, so when they enter a new date it will also have the character style and your STYLEREF fields would then pick up that new date.

Tagged :

Leave a Reply

Your email address will not be published. Required fields are marked *