VBA Word/Outlook - 客户用户窗体取消事件处理程序

3

我在关闭Word文档的事件处理函数中遇到了一些奇怪的行为。我在我的Outlook模块中使用Word的DocumentBeforeClose事件处理程序。在处理程序中,我会提示用户是否要完成文档、放弃文档或继续编辑。

如果我使用带有vbYesNoCancel按钮的MsgBox函数,则每次关闭Word文档时都会触发事件处理程序。这符合预期。

如果我使用自定义用户表单来代替,则事件处理程序只在第一次关闭Word文档时触发。如果用户选择“继续编辑”,那么下次关闭文档时,事件处理程序就不会触发了。

我不明白为什么这两种情况会导致不同的行为?为什么使用自定义用户表单会取消事件处理程序?

事件处理程序类(不起作用的版本)

Option Explicit

Private WithEvents mWordApp As word.Application

Private Sub mWordApp_DocumentBeforeClose(ByVal Doc As document, Cancel As Boolean)
    Dim msgBoxResponse As String
    
    'This code brings Outlook back to the active window so the user can response to the form
    AppActivate Application.ActiveExplorer.Caption
    SendKeys "%"
    
    Set finaliseUserForm = New UserFormFinaliseRFI
    finaliseUserForm.show
    msgBoxResponse = finaliseUserForm.response
    Unload finaliseUserForm
    Set finaliseUserForm = Nothing
    
    'msgBoxResponse = MsgBox("Do you want to finalise the document?", vbYesNoCancel)
    
    If msgBoxResponse = "Finalise" Then
    'If msgBoxResponse = vbYes Then
        Set mWordApp = Nothing
    Else
        Cancel = True
        AppActivate "test.docx"
    End If
End Sub

Public Sub StartEvents()
    Set mWordApp = CreateObject("Word.Application")
End Sub

Public Sub OpenWordDocument(filePath As String)
    mWordApp.Documents.Open filePath
    mWordApp.Visible = True
End Sub

事件处理程序类(工作版本)

Option Explicit

Private WithEvents mWordApp As word.Application

Private Sub mWordApp_DocumentBeforeClose(ByVal Doc As document, Cancel As Boolean)
    Dim msgBoxResponse As String
    
    'This code brings Outlook back to the active window so the user can response to the form
    AppActivate Application.ActiveExplorer.Caption
    SendKeys "%"
    
    'Set finaliseUserForm = New UserFormFinaliseRFI
    'finaliseUserForm.show
    'msgBoxResponse = finaliseUserForm.response
    'Unload finaliseUserForm
    'Set finaliseUserForm = Nothing
    
    msgBoxResponse = MsgBox("Do you want to finalise the document?", vbYesNoCancel)
    
    'If msgBoxResponse = "Finalise" Then
    If msgBoxResponse = vbYes Then
        Set mWordApp = Nothing
    Else
        Cancel = True
        AppActivate "test.docx"
    End If
End Sub

Public Sub StartEvents()
    Set mWordApp = CreateObject("Word.Application")
End Sub

Public Sub OpenWordDocument(filePath As String)
    mWordApp.Documents.Open filePath
    mWordApp.Visible = True
End Sub

测试模块子

Option Explicit

Private mEvents As WordEventsHelper

Public Sub testEvents()
    Set mEvents = New WordEventsHelper
    mEvents.StartEvents
    mEvents.OpenWordDocument "\(mypath)\test.docx"
    AppActivate "test.docx"
End Sub

用户表单代码

Private mResponse As String

Public Property Get response() As String
    response = mResponse
End Property

Private Sub CommandButtonFinalise_Click()
    mResponse = "Finalise"
    Me.Hide
End Sub

Private Sub CommandButtonDiscard_Click()
    mResponse = "Discard"
    Me.Hide
End Sub

Private Sub CommandButtonContinueEditing_Click()
    mResponse = "Continue Editing"
    Me.Hide
End Sub

你尝试过调试代码吗?使用用户表单能得到正确的结果吗? - Eugene Astafiev
是的,一切似乎都正常工作 - 包括用户表单。除了使用自定义用户表单时事件处理程序仅在第一次触发这个事实。 - user1357607
确保将“Cancel”参数设置为true,以便再次触发事件。 - Eugene Astafiev
我在事件处理函数的“else”部分将Cancel设置为true。无论是我的客户用户表单还是消息框,都可以正确执行。只是当我使用用户表单时,处理程序再也不会触发了。 - user1357607
尝试使用Unload方法而不是Hide - Eugene Astafiev
显示剩余2条评论
2个回答

0

我本想添加一条评论,但太长了。

请替换此部分:

Set finaliseUserForm = New UserFormFinaliseRFI
finaliseUserForm.show
msgBoxResponse = finaliseUserForm.response
Unload finaliseUserForm
Set finaliseUserForm = Nothing

使用:

With New UserFormFinaliseRFI
    .Show vbModal
    msgBoxResponse = .response
End With

没有必要调用UnloadSet finaliseUserForm = Nothing,因为您创建的新表单实例将在某个时刻被VB本身终止,而且Unload方法已经在各种Office版本中引起了错误和崩溃,应该避免使用。

此外,您可能希望防止用户单击表单上的X按钮。将以下代码添加到您的表单代码中:

'Handles click on the X (close) button
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = vbFormControlMenu Then
        Cancel = True
    End If
End Sub

忽略X按钮或类似的东西:

'Handles click on the X (close) button
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = vbFormControlMenu Then
        Cancel = True
        mResponse = "Discard"
        Me.Hide
    End If
End Sub

为了获得特定的行为。


1
谢谢。你的回答的第一部分是一个好建议 - 比我之前做的要干净得多。然而,它并没有解决问题(至少对我来说不是)。此外,你的代码中有一个错误,因为你引用了finaliseUserForm.response而不是只有.response。实际上,我有一个单独的函数,可以从所有自定义用户表单中删除“X”按钮。这与错误无关,所以它是我已经删除的不必要代码的一部分。 - user1357607
@user1357607 是的,抱歉,我在回答中直接编写了With块,没有经过VBA和测试。删除了finaliseUserForm部分。如果问题仍然存在,请检查两个事项:1)Application.EnableEvents是否开启; 2)第一个DocumentBeforeClose事件运行并被取消后,mWordApp是否仍具有实例/状态。 - Cristian Buse

0

使用用户表单而不是消息框时有几个方面需要注意。首先,用户表单不知道父窗口句柄。其次,用户表单不是模态的。第三,使用Unload而不是Hide。请参见更改加载项用户窗体的父工作簿窗口以获取更多信息。


我认为我的用户表单不使用(或不需要使用)父窗口句柄。我的用户表单代码中没有句柄。更改活动窗口并取消关闭事件的代码在事件处理程序中。我不确定卸载和隐藏的相关性是什么?我在我的用户表单中使用“隐藏”,这样它就不会在我能够获取mResponse值之前消失。即使在将Unload finaliseUserForm行添加到将finaliseUserForm设置为Nothing之前,错误仍然存在。 - user1357607
即使在用户窗体按钮单击代码中使用“Unload me”命令,不期望的行为仍然存在。 - user1357607
你的用户表单是模态的吗?它如何防止在表单关闭之前停止事件处理程序? - Eugene Astafiev
用户表单在打开时会暂停事件处理程序的执行。当我点击按钮并且它被隐藏和/或卸载时,执行将继续。 - user1357607
正确使用消息框的代码是什么样子的? - Eugene Astafiev
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接