excel - 在复制粘贴到另一个工作表时停止闪烁/重构 Excel ScreenUpdating false 的代码
问题描述
我是初学者,仍在学习编程 MS Excel VBA 宏。我需要社区的帮助来解决我在 Excel 上使用宏代码的问题。
Sub export_data()
With Application
.ScreenUpdating = False
.Calculation = xlManual 'sometimes excel calculates values before saving files
End With
Dim wsCopy As Worksheet
Dim wsDest As Worksheet
Dim wsDest2 As Worksheet
Dim lCopyLastRow As Long
Dim lDestLastRow As Long
Dim lDestLastRow2 As Long
Dim i As Long
Dim check As Long
'Set variables for copy and destination sheets
Set wsCopy = Workbooks("Book 1.xlsm").Worksheets("Sheet 1")
Set wsDest = Workbooks("Book 2.xls").Worksheets("Sheet 1")
Set wsDest2 = Workbooks("Book 2.xls").Worksheets("Sheet 2")
'1. Find last used row in the copy range based on data in column A
lCopyLastRow = wsCopy.Range("J10:J16").Find(what:="", LookIn:=xlValues).Offset(-1).Row
'2. Find first blank row in the destination range based on data in column A
'Offset property moves down 1 row
lDestLastRow = wsDest.Cells(wsDest.Rows.Count, "J").End(xlUp).Offset(1).Row
lDestLastRow2 = wsDest2.Cells(wsDest2.Rows.Count, "A").End(xlUp).Offset(1).Row
wsCopy.Unprotect "pass"
For i = 10 To 15
If Range("W" & i) <> "" And Range("S" & i) = "" Then
MsgBox "please fill column S"
GoTo protect
ElseIf Range("K" & i) <> "" And Range("X" & i) = "" Then
MsgBox "please fill column X"
GoTo protect
ElseIf Range("W" & i) <> "" And Range("Y" & i) = "" Then
MsgBox "please fill column Y"
GoTo protect
ElseIf Range("W" & i) <> "" And Range("AB" & i) = "" Then
MsgBox "please fill column AB"
GoTo protect
ElseIf Range("W" & i) <> "" And Range("AA" & i) = "" Then
MsgBox "please fill column AA"
GoTo protect
ElseIf Range("W" & i) <> "" And Range("AC" & i) = "" Then
MsgBox "please fill column AC"
GoTo protect
End If
Next i
If Range("W" & 10) <> "" And Range("AD" & 10) = "" Then
MsgBox "please fill column AD"
GoTo protect
End If
If WorksheetFunction.CountIf(wsDest2.Range("B10:B" & lDestLastRow2 - 1), wsCopy.Range("B10")) > 0 Then
check = MsgBox("Double?", _
vbQuestion + vbYesNo, "Double data")
If check = vbYes Then
GoTo export
Else
GoTo protect
End If
Else
GoTo export
End If
If Range("Q5") <> "" Then
check = MsgBox("sure?", _
vbQuestion + vbYesNo, "Manual override")
If check = vbYes Then
GoTo export
Else
GoTo protect
End If
Else
GoTo export
End If
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End With
export:
'3. Copy & Paste Data
For Each cell In wsCopy.Range("AB10:AB15")
cell.Value = UCase(cell.Value)
Next cell
wsDest.Rows(lDestLastRow & ":" & lDestLastRow + lCopyLastRow - 10).Insert shift:=xlShiftDown
wsDest.Range("A" & lDestLastRow) = WorksheetFunction.Max(wsDest.Range("A10:A" & lDestLastRow)) + 1
wsDest.Range("L" & lDestLastRow - 1).Copy
wsDest.Range("L" & lDestLastRow).Resize(lCopyLastRow - 9, 1).PasteSpecial Paste:=xlPasteFormulas
wsDest.Range("R" & lDestLastRow - 1).Copy
wsDest.Range("R" & lDestLastRow).Resize(lCopyLastRow - 9, 1).PasteSpecial Paste:=xlPasteFormulas
wsCopy.Range("B10:K" & lCopyLastRow).Copy
wsDest.Range("B" & lDestLastRow).PasteSpecial Paste:=xlPasteValues
wsCopy.Range("B10:K" & lCopyLastRow).Copy
wsDest.Range("B" & lDestLastRow).PasteSpecial Paste:=xlPasteValues
wsCopy.Range("M10:Q" & lCopyLastRow).Copy
wsDest.Range("M" & lDestLastRow).PasteSpecial Paste:=xlPasteValues
wsCopy.Range("S10:AF" & lCopyLastRow).Copy
wsDest.Range("S" & lDestLastRow).PasteSpecial Paste:=xlPasteValues
For Each cell In wsDest.Range("B" & lDestLastRow & ":B" & lDestLastRow + lCopyLastRow - 10)
cell.Value = wsCopy.Range("B10").Value
Next cell
'COPY DATA for book 2 sheet 2
wsDest2.Rows(lDestLastRow2).Insert shift:=xlShiftDown
wsDest2.Range("A" & lDestLastRow2) = wsDest2.Range("A" & lDestLastRow2 - 1).Value + 1
wsCopy.Range("B10:C10").Copy
wsDest2.Range("B" & lDestLastRow2).PasteSpecial Paste:=xlPasteValues
wsCopy.Range("E10:Z10").Copy
wsDest2.Range("E" & lDestLastRow2).PasteSpecial Paste:=xlPasteValues
wsCopy.Range("AD10:AF10").Copy
wsDest2.Range("AD" & lDestLastRow2).PasteSpecial Paste:=xlPasteValues
Dim r As Range, tabel As Range, xTabel As Range
Dim x As Integer, xMax As Long
'y As Long, yMax As Long
Dim textTabel As String
Set tabel = wsCopy.Range("d10:d" & lCopyLastRow)
Set r = wsDest2.Range("d" & lDestLastRow2)
xMax = tabel.Rows.Count
For x = 1 To xMax
Set xTabel = tabel.Range(Cells(x, 1), Cells(x, 1))
textTabel = Trim(xTabel.Text)
If x = 1 Then
textTabel = textTabel
'r.Offset(x - 1, 0).ClearContents
Else
textTabel = "& " & textTabel
End If
r = r & textTabel
Next x
Dim r2 As Range, tabel2 As Range, xTabel2 As Range
Dim x2 As Integer, xMax2 As Long
'y As Long, yMax As Long
Dim textTabel2 As String
Set tabel2 = wsCopy.Range("AC10:AC" & lCopyLastRow)
Set r2 = wsDest2.Range("AC" & lDestLastRow2)
xMax2 = tabel2.Rows.Count
For x2 = 1 To xMax2
Set xTabel2 = tabel2.Range(Cells(x2, 1), Cells(x2, 1))
textTabel2 = Trim(xTabel2.Text)
If x2 = 1 Then
textTabel2 = textTabel2
'r.Offset(x - 1, 0).ClearContents
Else
textTabel2 = "& " & textTabel2
End If
r2 = r2 & textTabel2
Next x2
Dim r3 As Range, tabel3 As Range, xTabel3 As Range
Dim x3 As Integer, xMax3 As Long
'y As Long, yMax As Long
Dim textTabel3 As String
Set tabel3 = wsCopy.Range("AA10:AA" & lCopyLastRow)
Set r3 = wsDest2.Range("AA" & lDestLastRow2)
xMax3 = tabel3.Rows.Count
For x3 = 1 To xMax3
Set xTabel3 = tabel3.Range(Cells(x3, 1), Cells(x3, 1))
textTabel3 = Trim(xTabel3.Text)
If x3 = 1 Then
textTabel3 = textTabel3
'r.Offset(x - 1, 0).ClearContents
Else
textTabel3 = "& " & textTabel3
End If
r3 = r3 & textTabel3
Next x3
Dim r4 As Range, tabel4 As Range, xTabel4 As Range
Dim x4 As Integer, xMax4 As Long
'y As Long, yMax As Long
Dim textTabel4 As String
Set tabel4 = wsCopy.Range("AB10:AB" & lCopyLastRow)
Set r4 = wsDest2.Range("AB" & lDestLastRow2)
xMax4 = tabel4.Rows.Count
For x4 = 1 To xMax4
Set xTabel4 = tabel4.Range(Cells(x4, 1), Cells(x4, 1))
textTabel4 = Trim(xTabel4.Text)
If x4 = 1 Then
textTabel4 = textTabel4
'r.Offset(x - 1, 0).ClearContents
Else
textTabel4 = "& " & textTabel4
End If
r4 = r4 & textTabel4
Next x4
'Optional - Select the destination sheet
wsDest.Activate
GoTo protect
protect:
wsCopy.protect "pass", _
AllowFormattingCells:=True, _
DrawingObjects:=True, _
contents:=True, _
Scenarios:=True
Workbooks("Book 2.xls").Save
Exit Sub
End Sub
我正在使用 Microsoft Office 2016。当我运行代码时,它运行良好,但仍然闪烁。这很令人不安,我担心它会减慢处理速度。
有什么想法可以在代码运行时停止闪烁吗?
解决方案
先说最简单的:
如果您打算进行 VBA 开发,请查看Rubberduckvba.com。这是一个插件,可以让编码变得更加容易,并教给您很多您不知道但希望您知道的知识。完全披露我是该组的贡献成员。
Option Explicit
未显示在您的代码中。另外,因为您的导出代码中有一个未声明的变量cell
,我假设您默认情况下没有打开它。在顶部的菜单工具>选项>编辑器选项卡>代码设置组>需要变量声明选中该框。这要求您Dim cell As Range
在使用变量之前拥有。启用该选项后,您将在运行代码之前收到未定义变量的编译错误。这可能看起来像一些小事,但打开这个选项,因为它会在以后让你头疼。
您将check
其用作消息框结果。不要将它声明为Long
,而是Dim check As VbMsgBoxResult
在您键入时以这种方式声明它,check=
您将获得智能感知和可用的枚举值。
您已""
用作空字符串的占位符。改为使用vbNullString
。它是一个内置常量,可让您知道此检查是有意的。这是因为""
可能或可能是一个具有值 的字符串,"CheckValue"
它删除了单词,只留下空引号。vbNullString
是明确的。
我留下了大部分变量名,以便您可以更轻松地跟随我所做的重构。请注意,像 r、x、xMax 这样的变量不会提供任何有用的信息来说明它们的用途。使用描述性变量名称。未来你会感谢你的。描述性变量使代码自我记录并且更易于阅读。
注释。评论可能是某些人的热门话题。我发现描述性变量需要更少的代码。代码本身应该说明正在做什么。您的评论“'1. Find last used row ...”正在准确地说明它在做什么。lastRowInCopyArea = copyWorksheet.Range().FooBar.Row
已经这么说了。保存评论以说明为什么要完成某事。从代码本身应该可以看出什么。
不需要匈牙利符号 (HN)。集成开发环境 (IDE) 可以通过菜单 Edit>Quick Info 告诉您变量的类型Ctrl+I。用字母表示类型会抑制可读性,并且是以前编码习惯的遗留物。好的变量名可以自行解决很多问题。
由于您正在处理字符串,因此您可以在导出部分的开头使用类型化UCase$()
函数而不是泛型函数。UCase()
您正在隐式使用事物。您Range(Foo)
正在隐式访问您所在的活动工作表。要查看此内容,请右键单击 Range 一词以调出上下文菜单并选择Definition。
执行此操作时,您可能会看到一个对话框,说明“无法跳转到'范围',因为它是隐藏的”,现在在该对话框下显示对象浏览器(绿色)。单击确定关闭对话框。在 Classes (Red) 或 Members (Blue) 窗格区域中单击鼠标右键,然后从上下文菜单中选择Show Hidden Members 。
单击右上角的内部关闭按钮或使用 关闭对象浏览器Ctrl+F4。现在将显示您的代码窗口。再次通过右键单击单词 Range 并选择 Show Definition 来调出上下文菜单。您将被带到隐藏的 Global 类和 Range 成员。
红色框显示灰色的类名Global
通常是隐藏的,并且Range
成员是访问的对象。为了避免这种隐式访问,使用工作表完全限定您的范围,或者 ActiveSheet.Range(Foo)
如果您确实想访问活动工作表。再次这样做是明确的,并表明这是故意的。
我们已经得到了 的左侧Range(Foo)
,那么另一侧呢?您还隐式访问了默认属性。你怎么想出来的?在上图中,橙色框中的 Range 一词为绿色,表示它是一个链接。单击它,您将被带到 Classes 窗格中的 Range,如下所示。Range 对象具有可以访问的成员,可以是方法(执行操作的事物)或属性(有关范围的信息)。
成员窗格显示您可以访问的这些成员。在“成员”窗格中向下滚动,直到_Default
显示该成员。当您不包括成员访问 IERange(Foo)
时,您正在访问该_Default
成员。由于您正在检查单元格的值,因此您可以使用它Range(Foo).Value2
来限定您的成员访问权限。
你的循环可以而且应该被巩固。取第一个循环并将其与其他循环进行比较。每当您复制/粘贴并将数字标识符添加到变量时,您都会有代码气味。它们每个的起始行都是 10,只有列不同。
Dim r As Range, tabel As Range, xTabel As Range
Dim x As Integer, xMax As Long
'y As Long, yMax As Long
Dim textTabel As String
Set tabel = wsCopy.Range("d10:d" & lCopyLastRow)
Set r = wsDest2.Range("d" & lDestLastRow2)
xMax = tabel.Rows.Count
For x = 1 To xMax
Set xTabel = tabel.Range(Cells(x, 1), Cells(x, 1))
textTabel = Trim(xTabel.Text)
If x = 1 Then
textTabel = textTabel
'r.Offset(x - 1, 0).ClearContents
Else
textTabel = "& " & textTabel
End If
r = r & textTabel
Next x
您需要将其拉入描述它正在做什么的自己的函数中。这样做将消除重复的代码。这样做的另一个好处是,如果你发现了一个错误,并且在任何调用/使用该函数的地方修复它,也将得到修复。
你的代码在做什么?它连接范围内的单元格以制作文本标签。让我们从 name 开始ConcatenateLabelFrom
。我看到你的变量r
每次都在循环中分配。您不需要这样做,只需在完成所有连接后即可。请记住,这将是用于目的地的范围。循环的逻辑可以浓缩为
Private Function ConcatenateLabelFrom(ByVal concatenateArea As Range) As String
Dim rowInArea As Integer
For rowInArea = 1 To concatenateArea.Rows.Count
Dim textTabel As String
textTabel = Trim(concatenateArea.Cells(rowInArea).Text)
If rowInArea = 1 Then
textTabel = textTabel
Else
textTabel = textTabel & "& " & textTabel
End If
Next
ConcatenateLabelFrom = textTabel
End Function
通过向参数提供参数来调用该函数,如下所示。缩进只是为了便于阅读。
wsDest2.Cells(lDestLastRow2, "d").Value2 = ConcatenateLabelFrom( _
wsCopy.Range( _
wsCopy.Cells(10, "d"), _
wsCopy.Cells(lCopyLastRow, "d") _
) _
)
不需要使用 GoTo 进行跳转。重组代码比使用 GoTo 更好。这样做将使您的代码流更加合乎逻辑。它还需要您考虑如何恢复Application.ScreenUpdating/Calculation
属性。
您可以通过将这些部分封装到它们自己的子中来做到这一点。您的 Protect 子将如下所示并通过Protect wsCopy, protectBook
. 出口也可以做类似的事情。
Private Sub Protect(ByVal worksheetToProtect As Worksheet, ByVal workbookToSave As Workbook)
worksheetToProtect.Protect "pass", _
AllowFormattingCells:=True, _
DrawingObjects:=True, _
contents:=True, _
Scenarios:=True
workbookToSave.Save
End Sub
你的部分有
您的屏幕闪烁似乎正在发生,因为您在导出之前恢复了屏幕更新和自动计算。您在那里进行复制和粘贴,这就是显示的内容。还记得我关于r
在循环中分配的评论吗?这是其中的一部分。在重新打开 ScreenUpdating 之前,您可以使用Application.Calculate计算所有打开的工作簿。与重构您的 GoTo 跳转一样,请考虑您希望您的工作簿系列事件如何发生并相应地对其进行编码。
还有更多可以建议的,但这应该足够开始了。
推荐阅读
- r - 如何在除一个向量之外的所有元素上循环函数并将结果存储在数据框的单独列中
- curl - 使用 -d 命令时卷曲意外令牌
- amazon-web-services - AWS EKS:用户无权执行:iam:CreateRole on resource
- css - 在实时 sass 编译器中编译时出现 Sass 无效 css 错误
- javascript - 在完成 xmlhttprequest 之前调用外部函数
- html - 无法始终如一地获得结果,chrome dev tools 中的文本发生变化,下拉选择器无法工作
- reactjs - 如何在 React Jodit 编辑器中通过 URL 输入禁用上传图像?
- azure-data-factory - 将 ForEach SQL 加载到 Azure 数据工厂中的单个 CSV
- angular - Angular:全局 ErrorHandler 中的多种材质小吃吧
- c++ - 在返回 char 数组的函数中返回警告的局部变量的地址