【问题标题】:Excel macro to pause and resume, or remember where stoppedExcel 宏暂停和恢复,或记住停止的位置
【发布时间】:2015-03-31 17:22:40
【问题描述】:

我在 Excel 中创建了一个宏,它基本上将来自 1 个工作表的所有数据外包出去,并将它们分开到它们所属的位置。但是,有时会出现需要手动更正的错误值(如果手动完成,则会在将数据重定向到单独的工作表的过程中立即发现)。

当找到这样的值时,它旁边的单元格被标记(以识别它是错误的),为用户弹出警告,但我也希望代码“暂停”,让用户手动更改值,然后在准备好后恢复,这是我不知道该怎么做的部分(暂停和恢复)。

操作的整个代码如下(还有另一个宏准备这些工作表,但现在并不重要)。

Private Sub Zaradi_Click()
    Dim wb As Workbook
    Dim ws As Worksheet
    Dim rngPlan As Range
    Dim pvtTable As PivotTable
    Dim pvtField As PivotField
    Dim pvtItem As PivotItem
    Dim i As Integer
    Dim vykon As Long
    Dim praca As String
    Dim meno As String
    Dim er As String
    Dim errArray(1 To 20) As String
    Dim mbResult As Integer
    Dim parySpolu As Integer

    Set wb = Workbooks("Zoznam plánov")

    er = "Nesedia páry!"

    mbResult = MsgBox("Tieto zmeny sú nezvratné. Potvrdte, že túto operáciu si prajete vykona?", _
    vbYesNoCancel)

    Select Case mbResult

        Case vbYes

            Workbooks("Kontrola plánov").Sheets("Summary").Activate

            meno = Workbooks("Kontrola plánov").Sheets("summary").Cells(2, 9)

            ' zoznam kontrolovanych planov
            Set rngPlan = Workbooks("Kontrola plánov").Sheets("Summary").Range(Cells(2, 1), Cells(10000, 1).End(xlUp))

            For i = 1 To rngPlan.Rows.Count ' pocet riadkov (size) kontrolovanych planov

                ' hodnota vykonu
                vykon = Workbooks("Kontrola plánov").Sheets("summary").Cells(i + 1, 6)
                ' co robil prace
                praca = Workbooks("Kontrola plánov").Sheets("summary").Cells(i + 1, 4)

                ' aktivuje pouzivany plan
                Set ws = wb.Sheets("Plán " & rngPlan(i))

                ws.Activate

                ' prida pracu
                ws.Cells(10000, 1).End(xlUp).Offset(1) = praca

                ' prida vykon
                ws.Cells(10000, 2).End(xlUp).Offset(1) = vykon

                ' prida meno
                ws.Cells(10000, 3).End(xlUp).Offset(1) = meno

                Set pvtTable = ws.PivotTables(1)
                Set pvtField = pvtTable.PivotFields(1)

                pvtTable.PivotCache.Refresh

                For j = 1 To pvtField.PivotItems().Count         

                    Set pvtItem = pvtField.PivotItems(j)                                    
                    pvtItem.ShowDetail = False                                        
                    ActiveSheet.PivotTables(1).NullString = "0"                                    
                    If pvtItem.Value = "(blank)" Then

                    Else
                        parySpolu = pvtTable.GetPivotData("Páry", "Práca", pvtField.PivotItems(j))
                        If parySpolu > ws.Cells(2, 7) Then
                            ws.Cells(j + 1, 11) = er
                            pvtItem.ShowDetail = True
                            MsgBox er
                        Else
                            ws.Cells(j + 1, 11) = "OK"
                        End If                             
                    End If
                Next j       
            Next i

            ' aktivuje sumarizaciu
            Workbooks("Kontrola plánov").Sheets("summary").Activate

        Case vbNo
            Exit Sub
        Case vbCancel
            Exit Sub
    End Select

    Workbooks("Kontrola plánov").Sheets(1).Activate
    MsgBox errNumbers

End Sub

发现错误值并给出警告的代码部分在这里:

If parySpolu > ws.Cells(2, 7) Then
    ws.Cells(j + 1, 11) = er
    pvtItem.ShowDetail = True
    MsgBox er
Else
    ws.Cells(j + 1, 11) = "OK"
End If

我已经对如何做到这一点提出了建议。一个是使用 InputBox,但我认为这对于这种情况并不理想(因为用户更愿意正确检查所有内容、源表、找到问题的根源等),所以我认为暂停和恢复会更好。另一个建议是这样做:

公共 lastCellChecked 作为字符串

Sub Check_Someting()

    Dim cell As     Excel.Range
    Dim WS As       Excel.Worksheet

    If Not lastCellChecked = vbNullString Then Set cell = Evaluate(lastCellChecked)

    '// Rest of code...

    '// Some loop here I'm assuming...
    lastCellChecked = "'" & WS.Name & "'!" & cell.Address
    If cell.Value > 10 Then Exit Sub '// Lets assume this is classed as an error
    '// Rest of loop here...

    lastCellChecked = vbNullString
End Sub

错误前最后一个单元格的地址被存储在哪里,宏在下一次运行时从那里继续(如果没有存储,它从头开始运行)。我认为这个解决方案更适合我的问题。然而,最后,我是一个非常缺乏经验的“程序员”,所以想知道什么是最有效/最好的方法(以及对我已经实现的代码的任何其他改进将不胜感激)。

【问题讨论】:

    标签: vba excel


    【解决方案1】:

    Excel 是事件驱动的。没有事件=没有行动。所以,基本上,问题是触发代码“继续”的事件应该是什么。

    第一个选项是(正如您所指出的)您使用输入框或表单,一旦输入更正,代码将继续。在这种情况下,会出现“单击按钮”事件来确认校正值。

    如果您想允许用户对工作表本身进行更改,那么除了捕获“Worksheet_Change”事件之外没有其他事件可以使用。因此,基本上,如果有需要更正的错误,您将不得不暂停/停止代码(并保存在一些需要更正的单元格的隐藏工作表上)。之后就可以使用了

    Private Sub Worksheet_Change(ByVal Target As Range)
    
    'Assuming that you "saved" the last position here:
    'SomeHiddenSheet.Range("A1").value2 = "$D$10"  <-- this is the location where an error occurred which needed fixing
    
    If Intersect(Target, SomeHiddenSheet.Range("A1").Value2) Is Nothing Then
        'The user did not change the requested cell but another
    Else
        'The user change the cell
    End If
    
    End Sub
    

    检查您请求的单元格是否已更改的事件。但是使用此解决方案,您会遇到代码停止的问题。用户可以更改您要求他/她更改的单元格。但不能保证。事实上,用户可能决定更改另一个单元格或根本不做任何事情而只是保存/关闭文件。因此,使用此解决方案,您可能会再次重新检查所有先前的单元格。

    【讨论】:

      【解决方案2】:

      我认为停止/恢复宏以允许人为干预没有任何意义,因为宏旨在使事情自动化。如果有人需要干预,他不妨重新运行宏,让它检查所需的整个范围。

      如果您遇到性能问题(例如,因为您有大量数据),您可能需要优化代码。

      所以,我希望您在出现错误时停止宏执行,并带有一条消息(!)警告用户不受欢迎的数据, - 更好 - 确保在问题发生之前检查错误

      【讨论】:

        【解决方案3】:

        使用全局变量,例如ProcessPaused As Boolean 和:

        ProcessPaused = True
        Do While ProcessPaused
            DoEvents
        Loop
        

        一旦你完成更正值 a

        Sub corrected()
            ProcessPaused = False
        End Sub
        

        将使您的宏继续。

        在您的案例中实现如下所示:

        If parySpolu > ws.Cells(2, 7) Then
            ws.Cells(j + 1, 11) = er
            pvtItem.ShowDetail = True
            MsgBox er
            ProcessPaused = True
            Do While ProcessPaused
                DoEvents
            Loop
        Else
            ws.Cells(j + 1, 11) = "OK"
        End If
        

        当然你必须放置一个大按钮:

        Sub PokracovatVProcese_Click()
            ProcessPaused = False
        End Sub
        

        【讨论】:

        • 您的宏实际上会停止,直到您通过运行 corrected() 触发继续,这也可能是按钮单击或工作表更改事件。我假设您可能需要在其他地方寻找值/检查其他值。确保启用屏幕更新(在给定的宏中它没有被禁用,所以没关系)。
        猜你喜欢
        • 1970-01-01
        • 2013-05-29
        • 1970-01-01
        • 2011-01-07
        • 2013-07-19
        • 2015-07-28
        • 2014-12-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多