【问题标题】:Word VBA code to fetch Drop Down list data用于获取下拉列表数据的 Word VBA 代码
【发布时间】:2020-10-28 23:42:12
【问题描述】:

我正在尝试在 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

如果有任何帮助修改我的循环代码以获取下拉列表结果,我们将不胜感激。

非常感谢!

【问题讨论】:

    标签: vba drop-down-menu ms-word word-contentcontrol


    【解决方案1】:

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

    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
    

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

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

    (其实你可以一次性搞定所有事情

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

    但我通常发现使用多个语句可以更轻松地进行调试)。

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

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

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

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

    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,而不是索引或文本,您可以在同一模块中执行类似的操作 /em> 这样你就设置了 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 倾向于断开内容控件与部件的连接。

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

    ' 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(*)]"

    【讨论】:

    • 您好,感谢您的回复。我想要做的是用以前保存的报告填充模板。我正在尝试从上次保存的版本中调用数据,然后使用以前的响应填充下拉列表。你是说这样做是不可能的吗?非常感谢,
    • 嗨@slightly snarky,谢谢,我研究了你的例子并让它工作。感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    相关资源
    最近更新 更多