【问题标题】:Determining if Save, Dont' Save or Cancel is Used in VBA确定是否在 VBA 中使用保存、不保存或取消
【发布时间】:2020-12-25 17:01:01
【问题描述】:

我正在使用 Workbook_BeforeSave 事件来检查是否在保存文档之前填写了所有必填单元格。如果有任何必填单元格为空,它将要求用户在保存文档之前填写它们。

代码按预期工作。但我面临的唯一问题是,当用户在未填写必填单元格的情况下单击 Close(X) 时,excel 会提示用户在关闭工作簿之前是否要保存/不保存更改或取消提示,如果用户点击保存,Workbook_BeforeSave 事件被调用,它通知用户有空的必填单元格需要填写并突然关闭文档。

我不想在用户单击“保存”时关闭工作簿,而必须填写必填单元格。如果他单击“不保存”,则关闭文档而不填写必填单元格。

如何做到这一点。

代码,

Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Dim wb As Workbook
Dim ws1 As Worksheet
Dim userange, userange1, userange2 As Range
Dim iCell, positionrng As Range
Dim usedrow As Long
Dim usecolumn As Long
Dim rowposition As Long
Dim ws1lastrow, lastcol As Long

Application.DisplayAlerts = False
Application.ScreenUpdating = False
Set wb = ThisWorkbook
Set ws1 = wb.Worksheets("CAPEX FY21-22")

rowposition = 9
criteria = ws1.Range("BM9").Value
investment = ws1.Range("BT9").Value
roi = ws1.Range("BU9").Value
justification = ws1.Range("BW9").Value

ws1lastrow = ws1.Cells(Rows.Count, criteria).End(xlUp).Row

If ws1.Cells(rowposition, criteria).Value = "" Or ws1.Cells(rowposition, criteria).Value = Empty Then
GoTo exiting:
End If

Set userange1 = ws1.Range(ws1.Cells(rowposition, criteria), ws1.Cells(ws1lastrow, investment))
Set userange2 = ws1.Range(ws1.Cells(rowposition, roi), ws1.Cells(ws1lastrow, justification))
Set userange = Union(userange1, userange2)


For Each iCell In userange

If IsEmpty(iCell) = True Then

MsgBox ("Document cannot be saved!" & vbCrLf & "Mandatory cell(s) are empty!" & vbCrLf & "Please fill the highlighted cell to save.")

iCell.Activate

ActiveCell.Interior.Color = RGB(255, 255, 0)

Application.Goto ActiveCell, Scroll:=True

Cancel = True

Exit Sub

GoTo exiting:

End If

Next


saves:

MsgBox ("Document saved")

exiting:
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub

【问题讨论】:

    标签: excel vba before-save


    【解决方案1】:

    当工作簿关闭时,会调用一个事件 BeforeClose。解决方案可能是将您的验证逻辑放在那里,并防止工作簿在验证不成功时关闭。

    Private Sub Workbook_BeforeClose(Cancel as Boolean)
        If IsValid = False Then
           Cancel = True
        End If
    End Sub
    
    Private Function IsValid() As Boolean
        ' Validation code goes here.
    End Function
    
    

    【讨论】:

    • 是否可以从 Workbook_BeforeSave 事件本身检查用户是否点击了保存或不保存或取消?
    【解决方案2】:

    我建议您重构您的代码。你的Workbook_BeforeSave 做的事情太多了。考虑如何在不同的子和功能之间划分职责。我这样说是因为Workbook_BeforeSave 中的大部分逻辑都是Workbook_BeforeClose 事件处理程序所需要的。

    我建议如下结构:

    编写一个函数,返回是否可以保存工作簿。此函数将包含您当前的几乎所有代码。您可以将其保存在标准模块中

    Function IsOKToSave() As Boolean
        ' Returns True if all mandatory cells are filled
        ' Otherwise, returns False
    End Function
    

    在您的事件处理程序中使用此函数。

    Private Sub Workbook_BeforeClose(Cancel As Boolean)
        Dim iResponse As Integer
            
        ' Handle the saving yourself
        ' first check if the workbook has changed since it was last saved
        If Not Me.Saved Then
            If IsOkToSave Then
                iResponse = MsgBox("Do you want to save changes to '" & Me.Name & "'?", vbYesNoCancel)
                If iResponse = vbYes Then
                    Application.EnableEvents = False
                    Me.Save
                    Application.EnableEvents = True
                ElseIf iResponse = vbCancel Then
                    Cancel = True
                Else
                    ' close without saving
                    Me.Saved = True
                End If
            Else
                iResponse = MsgBox("Document cannot be saved!" & vbCrLf & _
                                   "Mandatory cell(s) are empty!" & vbCrLf & vbCrLf & _
                                   "Do you want to close WITHOUT saving?", vbYesNo + vbDefaultButton2)
                If iResponse = vbYes Then
                    ' close without saving
                    Me.Saved = True
                Else
                    ' Cancel closing the workbook
                    Cancel = True
                End If
            End If
        End If
    End Sub
    
    Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
        If Not IsOkToSave Then
            Cancel = True
            MsgBox "Document cannot be saved!" & vbCrLf & _
                   "Mandatory cell(s) are empty!" & vbCrLf & _
                   "Please fill the highlighted cell to save."
        End If
    End Sub
    

    另外请注意,您需要声明每个变量的类型。因此,行

    Dim userange, userange1, userange2 As Range
    

    应该是

    Dim userange As Range, userange1 As Range, userange2 As Range
    

    否则,userangeuserange1 将是 Variant 类型

    【讨论】:

      【解决方案3】:

      有两个事件与您的计划相关。一个是Workbook_Close 事件,它是通过单击 X 或实际上是一些其他用户操作触发的。另一个是Workbook_Save 事件,它由导致保存工作簿的任何用户操作触发,包括SaveAs

      您已经为其中一个事件提供了服务,但没有为另一个提供服务。观察关闭操作通常(除非被抑制)会导致 Excel 检查Saved 属性并询问用户是否要保存,如果工作簿不干净。如果用户选择保存 `Workbook_Save~ 事件将触发,这将触发您的过程。

      为了更好地控制这些事件,您应该考虑为这些事件中的每一个设置一个均匀的过程,可能像这样:-

      Option Explicit
      
      Private Sub Workbook_BeforeClose(Cancel As Boolean)
      
          With ThisWorkbook
              If Not .Saved Then
                  Cancel = NotDone
                  If Not Cancel Then
                      .Save
                  End If
              End If
          End With
      End Sub
      
      Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
          Cancel = NotDone
      End Sub
      
      Private Function NotDone() As Boolean
          ' return True if mandatory cells aren't all filled
          
          ' =================================================
          ' basically, your existing code goes here
          ' =================================================
      End Function
      

      在这种情况下,您现有的代码进入函数 NotDone 几乎没有变化,但如果未填写必填字段(否则为 False),则返回 True。如果用户尝试关闭工作簿,Before_Close 过程会检查工作簿是否已保存并运行NotDone 以检查是否所有单元格都已填充(如果没有)。如果没有发现任何问题,程序会悄悄地保存工作簿,然后将其关闭。否则该动作被取消(函数NotDone中有一条关于此效果的消息。

      另一方面,如果用户只是尝试保存事件过程的Cancel 属性设置为NotDone 返回的值,从而允许保存操作继续或取消它。

      我试图帮助您使用该功能,但发现您的代码不够透明。请注意,Dim userange, userange1, userange2 As Range 仅生成 Range 数据类型的最后一项,其他项为变体。您必须为每个列出的变量单独分配一个数据类型。 rowposition = 9 后跟 criteria = Ws1.Range("BM9").Value 不是一个好习惯。使用变量或硬代码,但不能同时使用。没有适当的理由来抑制警报、屏幕更新或应用程序事件。这样做可能没有效果或不利影响,但永远不会有好处,因为没有针对任何效果。

      【讨论】:

      • 感谢您的建议。我以后会关注的。
      • @SuperSymmetry 我使用 EnableEvents 因为工作簿将 Worksheet_Change 事件用于其其他功能。是的,我必须注意使用它。
      猜你喜欢
      • 2015-09-23
      • 1970-01-01
      • 1970-01-01
      • 2019-05-18
      • 1970-01-01
      • 2017-02-16
      • 2013-03-22
      • 1970-01-01
      • 2019-07-01
      相关资源
      最近更新 更多