0

我正在尝试在 Word 中编写允许我从内容控制下拉列表中获取数据的代码。此数据是从以前保存的 Word 文件中提取的,我在脚本开始时引用了该文件(但此处不显示,因为这不是问题)。

我有这个适用于其他类型的内容控件(下面的示例),但我无法弄清楚这将如何适用于下拉列表。

这是我无效的代码:

For l = 1 To 28
Windows(ReportWindowName).Activate
TagName = "Rating" & l
Set doc = ActiveDocument
Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
cc.Range.Select
ccc = Selection.Text
OriginalDocument.Activate
TagName = "Rating" & l
Set doc = ActiveDocument
Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
cc.Range.Select
Selection.Text = ccc
Next l

代码在 Selection.Text 处失效。我需要修改一些内容以允许代码获取下拉列表中的条目。

下面是来自同一命令的另一个非常相似的代码,它可以工作,但从文本字段返回数据,而不是从 dame 文件中保存的下拉列表中返回数据:

For j = 1 To 6
Windows(ReportWindowName).Activate
TagName = "Mandatory" & j
Set doc = ActiveDocument
Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
cc.Range.Select
ccc = Selection.Text
OriginalDocument.Activate
TagName = "Mandatory" & j
Set doc = ActiveDocument
Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
cc.Range.Select
Selection.Text = ccc
Next j

希望有任何帮助修改我的循环代码以获取下拉列表结果。

非常感谢!

4

1 回答 1

0

如果您试图内容控件中获取文本,您最多需要的是

Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
' Let's just show the "display name"
Debug.Print cc.Range.Text

您可以将其缩短为

Set ccs = doc.SelectContentControlsByTag(TagName)
' Let's just show the "display name"
Debug.Print ccs(1).Range.Text

如果您愿意,甚至可以更进一步。

您目前拥有的代码失败的原因是它实际上是在尝试将文本放入内容控件中。您可以使用文本控件但不能使用下拉列表

(跟进您的评论)如果要将下拉列表设置为某个值,您基本上必须确定 DropDownListEntries 集合中的哪个项目是正确的,然后选择它。ContentControl 中的每个 DropDownListEntry 都有一个唯一的 Index、唯一的 Text(显示文本)和 Value(隐藏值)。

您可以通过查看源 ContentControl 的 .Range.Text 从下拉列表中获取文本,但不能将其用作目标 ContentControl 列表条目的索引,因此您必须迭代:

因此,如果 ccc 包含要显示的文本,则需要类似

Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
' This asumes you know this is a dropdown list cc
Dim ddle as Word.ContentControlListEntry
For Each ddle in cc.DropdownListEntries
  If ddle.Text = ccc Then
    ddle.Select
    Exit For
  End If
Next

或者,您可以从源代码管理中获取索引(并且您必须迭代源代码管理的侦听器才能做到这一点)。假设它在 variable 中idx。那么你所需要的就是

Set ccs = doc.SelectContentControlsByTag(TagName)
Set cc = ccs(1)
cc.DropdownListEntries(idx).Select

(事实上​​,你可以一次完成所有

doc.SelectContentControlsByTag(TagName)(1).DropDownlistEntries(idx).Select

但我通常发现使用多个语句使调试更容易)。

因此,使用这种方法,您必须迭代一组列表条目或另一组(或两者,如果您想使用 Value)。

另一种技术是将控件映射到 CustomXMLPart 中的 Element 并仅更新 Element 值。Word 然后将该值传播到映射到该元素的所有 ContentControls。有很多东西要学,看起来你不需要的复杂性,但是当你走到最后时,我希望你会明白为什么这实际上是一个非常简洁的方法。

在最简单的情况下,它是这样工作的。假设您的文档中有一个DropDown 内容控件。

然后您可以(重新)创建一个 XML 部件并将内容控件映射到它,就像这样。您只需要为一个文档执行一次这段代码。如果您的文件是基于模板或由其他文件的副本制作的,那么模板/原件就是一次。

Option Explicit
' A namespace URI can just be  a piece of text, but its better if you can use
' something that you "own" such as a domain name.
' There is nothing special about this name.
Const myNameSpace As String = "myns0"

Sub recreateCXPandMapCCs()
Dim ccs As Word.ContentControls
Dim cxp As Office.CustomXMLPart
Dim i As Integer
Dim r As Word.Range
Dim s As String
' There is nothing special about these element names.
' You can use your own
s = ""
s = s & "<?xml version=""1.0"" encoding=""UTF-8""?>" & vbCrLf
s = s & "<ccvalues1 xmlns='" & myNameSpace & "'>" & vbCrLf
s = s & "  <dropdown1/>" & vbCrLf
s = s & "</ccvalues1>"

With ActiveDocument
  ' select and delete any existing CXPs with this namespace
 For Each cxp In .CustomXMLParts.SelectByNamespace(myNameSpace)
    cxp.Delete
  Next
  
  ' Create a new CXP
  Set cxp = .CustomXMLParts.Add(s)

  ' Connect your dropdown. Instead, you can do this manually in the XML Mapping
  ' Pane in the Developer tab

  ' For an XML Part that only has one namespace the prefix mapping should always be "ns0". 
  .ContentControls(1).XMLMapping.SetMapping "/ns0:ccvalues[1]/ns0:dropdown1[1]", , cxp
  Set cxp = Nothing
End With
End Sub

然后,要设置 DropDown 的值(它需要是隐藏的Value,而不是索引或文本,您可以在同一个模块中执行类似的操作,以便设置 myNameSpace 常量。假设您想要设置常数值“xyzvalue”

Sub populateDropdown1Element()
With ActiveDocument.CustomXMLParts.SelectByNamespace(myNameSpace)(1)
  .SelectSingleNode("/ns0:ccvalues1[1]/ns0:dropdown1[1]").Text = "xyzvalue"
End With
End Sub

当然,如果源文档具有相同的映射,您可以从源文档的 XML 中的相同元素获取源文档下拉列表的值。事实是,如果您有相同的 XML、相同的映射等,理想情况下,您应该能够将目标文档中的整个 CustomXMLPart 替换为“源”文档中的一个。发明 CustomXMLParts 的原因之一是允许使用 Office Open XML SDK 的人们完全做到这一点。不幸的是,它在打开文档的 VBA 中不起作用,因为 Word 倾向于断开内容控件与部件的连接。

但是您可以做的是迭代所有元素和属性节点(例如)并将目标中的文本替换为源中的文本。像这样:

' You would need to pass in a reference to the document you want to get your data *from*
Sub replaceXML(sourceDocument As Word.Document)
Dim s As String
Dim cxn As Office.CustomXMLNode
Dim sourcePart As Office.CustomXMLPart

' You still need that definition of "myNameSpace"
Set sourcePart = sourceDocument.CustomXMLParts.SelectByNamespace(myNameSpace)(1)

With ActiveDocument
  For Each cxn In .CustomXMLParts.SelectByNamespace(myNameSpace).Item(1).SelectNodes("//*[not(*)] | //@*")
    cxn.Text = sourcePart.SelectSingleNode(cxn.XPath).Text
  Next
End With
End Sub

选择什么"//*[not(*)] | //@*"?好吧,"//*[not(*)]"选择叶子元素(包括具有属性的元素), "//@*"选择所有属性(始终是叶子节点)并且|基本上是“或”或“并集”。

我在 Word 中看到的大多数自定义 xml 仅将数据存储在 Elements 中,在这种情况下,您只需要"//*[not(*)]"

于 2020-07-08T15:02:15.063 回答