Actual Word VBA Help

G

Greg Maxey

Does anyone know if or where Microsoft has published any "real" help with
regards to working with ContentControls. Particularly the area of mapping?

For example the Help topic on: XMLMapping.SetMappingByNode Method

Provides the following example:

The following example sets the built-in document property for the document
author, inserts a new content control into the active document, and then
sets the XML mapping for the control to the built-in document property.

Visual Basic for Applications
Dim objcc As ContentControl
Dim objNode As CustomXMLNode
Dim objMap As XMLMapping
Dim blnMap As Boolean

ActiveDocument.BuiltInDocumentProperties("Author").Value = "David Jaffe"

Set objcc = ActiveDocument.ContentControls.Add _
(wdContentControlDate, ActiveDocument.Paragraphs(1).Range)

Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/package/2006/metadata/core-properties")
_
(1).DocumentElement.ChildNodes(1)

Set objMap = objcc.XMLMapping
blnMap = objMap.SetMappingByNode(objNode)


All well and good and works like a charm. The problem is that it offers no
real help on deciphering the gobbledygook contained in the "Set objNode"
line. Unless one read this example how would anyone guess how to craft that
statement? What if one wanted to map to another built-in docproperty. How
does one determine the "ChildNode" number of the other properties?
 
J

Jay Freedman

Hi Greg,

I have no idea where the documentation is, or whether it even exists. If it
does, it might be in one of the Open XML documents put out by ECMA, the
international body that approved the specification.

I just took the empirical approach: What can you find in an existing
document? This macro takes the custom XML node just above the one you used,
and lists all its (seven) child nodes in a table. You could beef it up to
display the actual XML and some other properties of each child node -- it's
instructive to display objChild in the Watch window while you single-step
through the loop.

Sub ListDocElementChildren()
Dim oTbl As Table
Dim counter As Long
Dim objNode As CustomXMLNode
Dim objChild As CustomXMLNode

Set oTbl = ActiveDocument.Tables.Add(ActiveDocument.Range, _
numrows:=1, numcolumns:=3)
With oTbl
.Cell(1, 1).Range.Text = "Child Index"
.Cell(1, 2).Range.Text = "BaseName"
.Cell(1, 3).Range.Text = "Text"
End With
counter = 1

Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/package/2006/metadata/core-properties")
_
(1).DocumentElement
For Each objChild In objNode.ChildNodes
oTbl.Rows.Add
counter = counter + 1
With oTbl.Rows(counter)
.Cells(1).Range.Text = CStr(counter - 1)
.Cells(2).Range.Text = objChild.BaseName
.Cells(3).Range.Text = objChild.Text
End With
Next
End Sub

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
G

Greg Maxey

Jay,

Maybe you can help a bit more ;-). I did tinker with this a little last
night and I have hit a stumbling block. I figured out how to list the
extended-properties in a manner similiar to your method of listing the
core-properties. Next I added a customdocumentproperty. After doing this I
unzipped the file and took a look in the docProp folder. Sure enough there
was an additional folder named "Custom.xml" right there with the existing
Core.xml and App.xml files. the Custom.xml file contained the
customdocumentpropert data. Next I ran this code:

Sub ListDocElementChildren()
Dim oTbl As Table
Dim counter As Long
Dim objNode As CustomXMLNode
Dim objChild As CustomXMLNode
ActiveDocument.Range.Delete
Set oTbl = ActiveDocument.Tables.Add(ActiveDocument.Range, numrows:=1,
numcolumns:=3)
With oTbl
.Cell(1, 1).Range.Text = "Child Index"
.Cell(1, 2).Range.Text = "BaseName"
.Cell(1, 3).Range.Text = "Text"
End With
counter = 1
Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/package/2006/metadata/core-properties")(1).DocumentElement
For Each objChild In objNode.ChildNodes
oTbl.Rows.Add
counter = counter + 1
With oTbl.Rows(counter)
.Cells(1).Range.Text = CStr(counter - 1)
.Cells(2).Range.Text = objChild.BaseName
.Cells(3).Range.Text = objChild.Text
End With
Next
ActiveDocument.Range.InsertAfter vbCr
ActiveDocument.Bookmarks("\endofdoc").Select
Set oTbl = ActiveDocument.Tables.Add(Selection.Range, numrows:=1,
numcolumns:=3)
With oTbl
.Cell(1, 1).Range.Text = "Child Index"
.Cell(1, 2).Range.Text = "BaseName"
.Cell(1, 3).Range.Text = "Text"
End With
counter = 1
Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties")(1).DocumentElement
For Each objChild In objNode.ChildNodes
oTbl.Rows.Add
counter = counter + 1
With oTbl.Rows(counter)
.Cells(1).Range.Text = CStr(counter - 1)
.Cells(2).Range.Text = objChild.BaseName
.Cells(3).Range.Text = objChild.Text
End With
Next
ActiveDocument.Range.InsertAfter vbCr
ActiveDocument.Bookmarks("\endofdoc").Select
Set oTbl = ActiveDocument.Tables.Add(Selection.Range, numrows:=1,
numcolumns:=3)
With oTbl
.Cell(1, 1).Range.Text = "Child Index"
.Cell(1, 2).Range.Text = "BaseName"
.Cell(1, 3).Range.Text = "Text"
End With
counter = 1
On Error GoTo Err_Handler
'Throws error
Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/officeDocument/2006/custom-properties")(1).DocumentElement
Set objNode = ActiveDocument.CustomXMLParts(3).DocumentElement
For Each objChild In objNode.ChildNodes
oTbl.Rows.Add
counter = counter + 1
With oTbl.Rows(counter)
.Cells(1).Range.Text = CStr(counter - 1)
.Cells(2).Range.Text = objChild.BaseName
.Cells(3).Range.Text = objChild.Text
End With
Next
Exit Sub
Err_Handler:
ShowNameSpaces
Resume Next
End Sub


Sub ShowNameSpaces()
Dim oXMLPart As CustomXMLPart
MsgBox ActiveDocument.CustomXMLParts.Count
For Each oXMLPart In ActiveDocument.CustomXMLParts
MsgBox oXMLPart.NamespaceURI
Next
MsgBox "You see custom-properties is not listed."
End Sub


As you can see, I set the objNode to what appears to be the namespace of the
custom.xml file. However, based on the error handling I have provided you,
it appears that the custom.xml file does not exists as a customXMLpart. Any
ideas? Thanks.
 
P

Peter Jamieson

As you can see, I set the objNode to what appears to be
the namespace of the custom.xml file. However, based on
the error handling I have provided you,
it appears that the custom.xml file does not exists as
a customXMLpart. Any ideas? Thanks.

FWIW
a. I believe this is the correct conclusion. i.e., you will never be
able to get to the Custom Properties via this route, because the Custom
Properties have not been deliberately added into the set of custom
parts. Of course there may be a "back door" somewhere (e.g. some other
way to specify the namespace), but I have never seen any evidence of it.
b. the documentation at
http://msdn.microsoft.com/en-us/library/aa433854.aspx

says

<<
There are three default parts that are always created with a document.
These are 'Cover pages', 'Doc properties' and 'App properties'. The last
two were in previous versions of Microsoft Word but are now provided in
XML form in the CustomXMLParts object collection
which reinforces the notion that the other two types of property are
/specially treated/ as Custom XML parts (i.e. this is "by design") and
it is difficult to escape the conclusion that the Custom document
properties have not been specially treated in this way, also "by
design". This is very probably because Microsoft has been focussed on
interop. between Word and SharePoint (and perhaps as a byproduct,
InfoPath) and the newer XML-based property mechanism rather than the old
COM-based mechanism.

FWIW Microsoft has had several "goes" at "Document properties", including
a. the original pre-COM properties in Word 2
b. Word 6(?) and later properties based on COM "Structured Storage"
c. NTFS properties
d. Exchange properties, introduced one or two versions after the
original Exchange. No idea what has happened to those since)
e. XML-based properties, cf. the ones under discussion.

Interop has never been particularly good - I would say most
organisations that have tired to make good use of properties have
probably gone through a sequence of one-off conversions to deal with
whatever new technology they have decided to standardise on.

Maybe the fact that cover pages parts are also special is useful news
for you? :)


Peter Jamieson

http://tips.pjmsn.me.uk
 
G

Greg Maxey

Peter,

Thanks for your informative reply. I will continue to tinker and see if I
can find that backdoor ;-)
 
P

Pesach Shelnitz

I think that you may be assuming that there is some kind of built-in
relationship between CustomDocumentProperties and CustomXMLParts. The first
thing to note is that, by default, there no CustomDocumentProperties are
defined in a new Word 2007 document, as the following macro will demonstrate.

Sub ListCustomDocProperties()
Dim prop As DocumentProperty

MsgBox ActiveDocument.CustomDocumentProperties.Count
For Each prop In ActiveDocument.CustomDocumentProperties
Selection.TypeText Text:=prop.Name & " = " & prop.Value & vbCrLf
Next
End Sub

You didn't specify exactly what you did when you "added a
customdocumentproperty," but unless you added a new CustomXMLPart and
populated it with your "custom-properties" namespace, there is no reason to
expect a call to SelectByNamespace to successfully find a CustomXMLPart with
such a namespace.

The following macro may demonstrate what I think you may have been trying to
do.

Sub Demo()
Dim oTbl As table
Dim counter As Long
Dim i As Integer
Dim xmlText As String
Dim objNode As CustomXMLNode
Dim objChild As CustomXMLNode

xmlText = "<Properties xmlns=""http://schemas.openxmlformats.org/" & _
"officeDocument/2006/custom-properties"">" & _
"<CustomerName>John Doe</CustomerName>" & _
"<OrderNumber>1234</OrderNumber></Properties>"
Set oTbl = ActiveDocument.Tables.Add(Selection.Range, _
NumRows:=1, NumColumns:=3)
With oTbl
.Cell(1, 1).Range.Text = "Child Index"
.Cell(1, 2).Range.Text = "BaseName"
.Cell(1, 3).Range.Text = "Text"
End With
counter = 1

i = ActiveDocument.CustomXMLParts.Count
ActiveDocument.CustomXMLParts.Add
ActiveDocument.CustomXMLParts(i + 1).LoadXML (xmlText)
Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _

("http://schemas.openxmlformats.org/officeDocument/2006/custom-properties") _
(1).DocumentElement
For Each objChild In objNode.ChildNodes
oTbl.Rows.Add
counter = counter + 1
With oTbl.Rows(counter)
.Cells(1).Range.Text = CStr(counter - 1)
.Cells(2).Range.Text = objChild.BaseName
.Cells(3).Range.Text = objChild.Text
End With
Next
End Sub
 
G

Greg Maxey

Pesach,

Thanks for the helpful information.

What I did was this.

1. I started with a new blank document saved a Test.docm
2. I unzipped the file package and looked in the docProps folder.
3. There I found to files app.xml and core.xml
4. I zipped up the file package, reopened Word and used Office
Menu>Prepare>Properties>Advanced properties and assigned a value to the
"Publisher"
5. I unzipped the file package and looked in the docProps folder and saw a
new file had been created. This file is named custom.xml.
6. This file contained the following information:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
- <Properties
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
- <property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2"
name="Publisher">
<vt:lpwstr>Jimmy Cracked Corn</vt:lpwstr>
</property>
</Properties>

Since: Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/package/2006/metadata/core-properties")(1).DocumentElement

Seemed to work with the core.xml file and since:

Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties")(1).DocumentElement

seemed to work with the app.xml file, I assumed or expected that:

Set objNode = ActiveDocument.CustomXMLParts.SelectByNamespace _
("http://schemas.openxmlformats.org/officeDocument/2006/custom-properties")(1).DocumentElement

would have worked for the new file named custom.xml.

Do you know if it is possilbe to point to that custom.xml file located in
the docProps folder and and use it as the source of text in your xmlText
statement?

E.g., Insted of:

xmlText = "<Properties xmlns=""http://schemas.openxmlformats.org/" & _
"officeDocument/2006/custom-properties"">" & _
"<CustomerName>John Doe</CustomerName>" & _
"<OrderNumber>1234</OrderNumber></Properties>"

xmlText = "Some pointer to the docProps\custom.xml file text."

Thanks.
 
G

Greg Maxey

I think that I have found the backdoor ;-). The process is rather crude and
hopefully there is a better way. Basically I mimicked a process provided by
Jonathan West for accessing the images folder in the Office OpenXML File
folder. As copy of the document is saved to a temporary folder and
unzipped. This lets me point a CustomXLMParts.Load method to the custom.xml
file located in the docProps folder. Once the xml is loaded into a
customXMLPart then the document properties can evaluated similiarily to the
core and extended properties. When completed the temporary files are
destroyed.

Thanks Jay, Peter, and Pesach for your interest and assitance. If any of
you know of a better way using VBA to access the docProps\custom.xml file I
hope you will share.



Option Explicit
Dim vZipFile As Variant
Dim vFolder As Variant
Dim FSO As Object
Sub Demo()
Dim oTbl As Table
Dim counter As Long
Dim i As Long
Dim xmlText As String
Dim objNode As CustomXMLNode
Dim objChild As CustomXMLNode
If ActiveDocument.CustomDocumentProperties.Count = 0 Then Exit Sub
ClearCustomParts
Set oTbl = ActiveDocument.Tables.Add(Selection.Range, 1, 3)
With oTbl
.Cell(1, 1).Range.Text = "Child Index"
.Cell(1, 2).Range.Text = "Property Name"
.Cell(1, 3).Range.Text = "Value"
End With
counter = 1
i = ActiveDocument.CustomXMLParts.Count
ActiveDocument.CustomXMLParts.Add
xmlText = GetCustomXMLFile & "docProps\custom.xml"
'ActiveDocument.CustomXMLParts(i + 1).LoadXML ()
ActiveDocument.CustomXMLParts(i + 1).Load (xmlText)
Set objNode =
ActiveDocument.CustomXMLParts.SelectByNamespace("http://schemas.openxmlformats.org/"
_
& "officeDocument/2006/custom-properties")(1).DocumentElement
For Each objChild In objNode.ChildNodes
oTbl.Rows.Add
counter = counter + 1
With oTbl.Rows(counter)
.Cells(1).Range.Text = CStr(counter - 1)
.Cells(2).Range.Text = GetPropertyName(objChild.XML)
.Cells(3).Range.Text = objChild.Text
End With
Next
Set FSO = CreateObject("scripting.filesystemobject")
FSO.deletefolder Left(vFolder, Len(vFolder) - 1), True
End Sub

Function GetCustomXMLFile() As String
Call UnZip_Document_OpenXML_DataStore
GetCustomXMLFile = vFolder
End Function

Sub UnZip_Document_OpenXML_DataStore()
Dim oDoc1 As Word.Document
Dim pTempPath As String
Dim pTempFileName As String
Dim pExt As String
Application.ScreenUpdating = False
Set oDoc1 = ActiveDocument
'Define path for folder used to store a copy of the document OpenXML Format
file e.g., Username\Documents and Settings\Local Setting\Temp
pTempPath = Environ$("temp") & "\"
'Define a unique name for the file copy.
pTempFileName = oDoc1.Name & " " & Format(Now, "dd-mmm-yy")
'Retrieve the file extenstion
pExt = "." & LCase(Right(oDoc1.Name, Len(oDoc1.Name) - InStrRev(oDoc1.Name,
".", , 1)))
'Make a copy of the document and store it in the temporary folder
WordBasic.CopyFileA FileName:=ActiveDocument.FullName, _
Directory:=pTempPath & pTempFileName & pExt
'Create a zip extension
vZipFile = pTempPath & pTempFileName & ".zip"
'Rename the file copy with the zip extension
Name pTempPath & pTempFileName & pExt As vZipFile
'Unzip the file copy
Call UnzipMe
'Delete the file
Kill vZipFile
Application.ScreenUpdating = True
End Sub

Sub UnzipMe()
Dim FSO As Object
Dim oApp As Object
Dim Fname As Variant
Dim DefPath As String
Dim strDate As String
'Root folder for the new folder.
DefPath = Environ("Temp")
If Right(DefPath, 1) <> "\" Then
DefPath = DefPath & "\"
End If
'Create a unique folder to hold the unzipped contents
strDate = Format(Now, "mmddyyhmmss")
vFolder = DefPath & "MyUnzipFolder" & strDate & "\"
'Make the normal folder in DefPath
MkDir vFolder
'Extract the files into the newly created folder
Set oApp = CreateObject("Shell.Application")
oApp.Namespace(vFolder).CopyHere oApp.Namespace(vZipFile).items
On Error Resume Next
Set FSO = CreateObject("scripting.filesystemobject")
FSO.deletefolder Environ("Temp") & "\Temporary Directory*", True
End Sub

Function GetPropertyName(pStr As String) As String
Dim i As Long
Dim j As Long
Dim x As Long
i = InStr(pStr, "name=")
j = InStr(pStr, "><vt:lp")
x = j - (i + 6)
GetPropertyName = Mid(pStr, i + 6, x - 1)
End Function
 
C

Cindy M.

Hi Greg,
Next I added a customdocumentproperty.
FWIW I tried linking the CustomDocumentProperties to a
ContentControl during the 2007 beta and, after struggling
with it for a few days, reported it as a bug. The reply I
got back was "as designed" - they just didn't build the
access to that XML Part into the Content Control design.

Cindy Meister
INTER-Solutions, Switzerland
http://homepage.swissonline.ch/cindymeister (last update
Jun 17 2005)
http://www.word.mvps.org

This reply is posted in the Newsgroup; please post any
follow question or reply in the newsgroup and not by e-mail
:)
 
J

Jay Freedman

Cindy said:
Hi Greg,

FWIW I tried linking the CustomDocumentProperties to a
ContentControl during the 2007 beta and, after struggling
with it for a few days, reported it as a bug. The reply I
got back was "as designed" - they just didn't build the
access to that XML Part into the Content Control design.

Cindy Meister
INTER-Solutions, Switzerland
http://homepage.swissonline.ch/cindymeister (last update
Jun 17 2005)


This reply is posted in the Newsgroup; please post any
follow question or reply in the newsgroup and not by e-mail
:)

It's a shame they don't have reply classifications like "it's a defective
design but we don't have time to fix it now" and "why in the world would you
want to do that?". <g>

--
Regards,
Jay Freedman
Microsoft Word MVP
Email cannot be acknowledged; please post all follow-ups to the newsgroup so
all may benefit.
 
C

Cindy M.

Hi Jay,
It's a shame they don't have reply classifications like "it's a defective
design but we don't have time to fix it now" and "why in the world would you
want to do that?". <g>
Especially the latter <LOL>

Cindy Meister
 
G

Greg Maxey

Well it seems odd that a new file custom.xml is created in the folder
docProps right along with the existing files app.xml and core.xml. app.xml
and core.xml are treated as CustomXMLParts while custom.xml isn't.

Thanks.
 
P

Peter Jamieson

BTW, while we're in this general area, can anyone confirm/deny that the
"XPath expression" that can be specified for a Content Control can only
be an expression that identifies one (or perhaps more than one) node,
and cannot be an expression that invokes an xpath function, such as
concat(node1,node2) ?

I've tried using such functions but Word seems not to be able to
evaluate them, which suggests that either the term "XPath expression" is
generally taken (within the XPath world) to mean "an expression that
identifies a set of nodes" or that what is possible should be better
qualified in the documentation.

Not that XPath 1 has a particularly rich set of functions anyway...

Peter Jamieson

http://tips.pjmsn.me.uk
 
P

Peter Jamieson

Anyone?

Peter Jamieson

http://tips.pjmsn.me.uk

Peter said:
BTW, while we're in this general area, can anyone confirm/deny that the
"XPath expression" that can be specified for a Content Control can only
be an expression that identifies one (or perhaps more than one) node,
and cannot be an expression that invokes an xpath function, such as
concat(node1,node2) ?

I've tried using such functions but Word seems not to be able to
evaluate them, which suggests that either the term "XPath expression" is
generally taken (within the XPath world) to mean "an expression that
identifies a set of nodes" or that what is possible should be better
qualified in the documentation.

Not that XPath 1 has a particularly rich set of functions anyway...

Peter Jamieson

http://tips.pjmsn.me.uk
 
C

Cindy M.

Hi Peter,
BTW, while we're in this general area, can anyone confirm/deny that the
"XPath expression" that can be specified for a Content Control can only
be an expression that identifies one (or perhaps more than one) node,
and cannot be an expression that invokes an xpath function, such as
concat(node1,node2) ?

I can't confirm that, but I suspect it's the case. You might want to email
Tony Jollans about some experiences he's had trying to use "special" XML
in conjunction with Word.

Cindy Meister
INTER-Solutions, Switzerland
http://homepage.swissonline.ch/cindymeister (last update Jun 17 2005)
http://www.word.mvps.org

This reply is posted in the Newsgroup; please post any follow question or
reply in the newsgroup and not by e-mail :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top