【问题标题】:Implement auto-increment with Word macro使用 Word 宏实现自动增量
【发布时间】:2009-05-19 21:08:08
【问题描述】:

我正在为文档模板编写 Word/VBA 宏。每次用户从模板保存/创建新文档时,文档都需要在文本中嵌入一个 ID。我如何(尽可能简单)为此 ID 实现自动增量? ID 是数字。

系统必须有某种机制来避免不同的文档获得相同的 ID,但负载非常低。大约 20 人将使用此模板(在我们的 Intranet 上),每周总共创建大约 20 个新文档。

我曾想过拥有一个可以从宏中锁定和解锁的文本文件,或者使用 SQLite 数据库调用 PHP 页面,但还有其他更智能的解决方案吗?

请注意,我不能使用 UUID 或 GUID,因为这些 ID 需要可供人类和机器使用。我们的客户必须能够通过电话说:“......那么,关于这个,ID 436......?”

【问题讨论】:

  • 那么你真正想要的是每个存档和每个用户都有一个唯一的 ID,对吧?
  • 不,每个文档都有一个唯一的 ID。
  • 也许您应该改写“每次用户保存新文档时...”,因为它确实暗示正在进行保存计数过程...(我知道它没有' t)

标签: vba ms-word


【解决方案1】:

对此进行进一步思考,这是您可能要考虑的另一种方法。如果您对以前 ID 的目录不感兴趣,那么您可以简单地使用自定义文档属性来存储最后使用的 ID。

在 Word 97-2003 中,您可以通过转到“文件/属性”,选择自定义选项卡并在其中指定名称和值来添加自定义属性。在 Word 2007 中添加自定义文档属性有点难以理解,我认为它是“Office 按钮/准备/文档属性”,选择高级属性的小下拉框,您会得到相同的结果ol' 2007 年之前的对话。

在下面的示例中,我简单地将我的称为“DocumentID”,并为其分配了一个初始值零。

更新自定义文档属性的相关代码是:

ThisDocument.CustomDocumentProperties("DocumentID").Value = NewValue

作为概念证明,我创建了一个 .dot 文件并在 Document_New() 事件中使用了以下代码:

Sub UpdateTemplate()

    Dim Template    As Word.Document
    Dim NewDoc      As Word.Document
    Dim DocumentID  As DocumentProperty
    Dim LastID      As Integer
    Dim NewID       As Integer

    'Get a reference to the newly created document
    Set NewDoc = ActiveDocument

    'Open the template file
    Set Template = Application.Documents.Open("C:\Doc1.dot")

    'Get the custom document property
    Set DocumentID = Template.CustomDocumentProperties("DocumentID")

    'Get the current ID
    LastID = DocumentID.Value

    'Use any method you need for determining a new value
    NewID = LastID + 1

    'Update and close the template
    Application.DisplayAlerts = wdAlertsNone
    DocumentID.Value = NewID
    Template.Saved = False
    Template.Save
    Template.Close

    'Remove references to the template
    NewDoc.AttachedTemplate = NormalTemplate

    'Add your ID to the document somewhere
    NewDoc.Range.InsertAfter ("The documentID for this document is " & NewID)
    NewDoc.CustomDocumentProperties("DocumentID").Value = NewID

End Sub

祝你好运!

【讨论】:

  • 这个很巧妙,可惜编辑原始模板导致其他问题。
  • 对我来说效果很好,谢谢!我很好奇你为什么需要这些线路,以及它们的作用:Application.DisplayAlerts = wdAlertsNone; Template.Saved = False;另外,为什么需要删除对模板的引用?
  • 有谁知道,有没有办法让打开模板文件 (Set Template = Application.Documents.Open("C:\Doc1.dot")) 的行使用相对路径。或者也许还有另一种方法可以获取用于创建文档的模板文件的路径?有点想这样做,以便用户可以在不破坏代码的情况下移动文件。
【解决方案2】:

您可以使用 Word 和 Excel(或者我想是 Access,但我对使用 Access 有一种不自然的反感)完全通过 VBA 来处理这个问题。

首先,创建一个新的 Excel 工作簿并将其存储在您可以通过 Word 文档访问的位置(我的是 C:\Desktop\Book1.xls)。您甚至可能希望通过在单元格 A1 中输入数值来播种这些值。

在您的 word 文档中,您可以将其输入到 Document_Open() 子例程中:

Private Sub Document_Open()

Dim xlApp       As Excel.Application
Dim xlWorkbook  As Excel.Workbook
Dim xlRange     As Excel.Range
Dim sFile       As String
Dim LastID      As Integer
Dim NewID       As Integer

'Set to the location of the Excel "database"
sFile = "C:\Desktop\Book1.xls"

'Set all the variables for the necessary XL objects
Set xlApp = New Excel.Application
Set xlWorkbook = xlApp.Workbooks.Open(sFile)

'The used range assumes just one column in the first worksheet
Set xlRange = xlWorkbook.Worksheets(1).UsedRange

'Use a built-in Excel function to get the max ID from the used range
LastID = xlApp.WorksheetFunction.Max(xlRange)

'You may want to come up with some crazy algorithm for
'this, but I opted for the intense + 1
NewID = LastID + 1

'This will prevent the save dialog from prompting the user
xlApp.DisplayAlerts = False

'Add your ID somewhere in the document
ThisDocument.Range.InsertAfter (NewID)

'Add the new value to the Excel "database"
xlRange.Cells(xlRange.Count + 1, 1).Value = NewID

'Save and close
Call xlWorkbook.Save
Call xlWorkbook.Close

'Clean Up
xlApp.DisplayAlerts = True
Call xlApp.Quit
Set xlWorkbook = Nothing
Set xlApp = Nothing
Set xlRange = Nothing

End Sub

我意识到这是一个艰巨的过程,所以一定要根据自己的内心重新考虑它。这只是我发起的一个快速测试。此外,您需要通过 VBA 中的引用添加对 Excel 对象库的引用。如果您对它的工作原理有任何疑问,请告诉我。

希望有帮助!

【讨论】:

  • 我认为这不会解决来自不同用户的并发保存问题。你可以做得更好。
  • 如问题中所述,20 位用户每周将节省大约 20 次。并发在这里不是一个大问题。可能甚至不需要共享 Excel 文件。如果它已打开,只需抛出一个错误,然后重试。该方法无法扩展,但应该满足要求。
【解决方案3】:

你必须在某处存储下一个 ID 号。文本文件的想法和任何东西一样好。您只需要处理由于某种原因而被锁定或无法访问的可能性。

为一个号码使用数据库是多余的。

【讨论】:

    【解决方案4】:

    在我的头顶:

    • 通过自动化将 Excel 用作您的外部数据库。
    • 探索几个SQLite COM wrappers(想到Litex)。

    【讨论】:

    • 通过我们这里的骨头!详细说明任何一个,我们都会从中学习。
    【解决方案5】:

    “我从宏中锁定和解锁的文本文件”将是最安全的方法。 DOCID 文件只有一个数字:最后一个实际使用的 ID。

    A)您读取文件(不是在写入/追加模式下)并将文件存储在文档 DOC_ID =FILE_ID+1 上的变量中并保存文档。暂时你杀死 DOCID 文件,打开/创建以读写你的 DOC_ID。关闭文件。如果一切顺利,包括关闭,您就安全了,否则,返回 A)。

    您可能需要考虑:如果未找到文件,则使用此文档 ID +100 创建它,作为在 A) 中从无 UPS 灾难中恢复的衡量标准

    我太累了,无法检查它是否会在并发场景下造成死锁......它可能会。

    如果你觉得值得,我可以在这里放代码。

    【讨论】:

    • 抱歉,它必须是增量 ID,并且 ID 标识文档,而不是修订/保存。此外,ID 必须是人性化的,如上所述。
    【解决方案6】:

    看来我找到了一种方法来打开和更新一个具有独占权限的文本文件,这意味着不会有并发问题:

    Private Function GetNextID(sFile As String) As Integer
        Dim nFile As Integer
    
        nFile = FreeFile
    
        On Error Resume Next
        Open sFile For Binary Access Read Write Lock Read Write As #nFile
        If Err.Number <> 0 Then
            ' Return -1 if the file couldn't be opened exclusively
            GetNextID = -1
            Err.Clear
            Exit Function
        End If
        On Error GoTo 0
    
        GetNextID = 1 + Val(Input(LOF(nFile), #nFile))
        Put #nFile, 1, CStr(GetNextID)
        Close #nFile
    End Function
    

    只需调用此函数,直到它不再返回 -1。整洁。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-15
      • 2019-11-05
      • 1970-01-01
      • 2018-01-05
      相关资源
      最近更新 更多