patternMinor
Copy data to another worksheet
Viewed 0 times
dataworksheetanothercopy
Problem
I have the following but it takes long time to run, it is a simple wherein the user select a file and data from Sheet1 is copied to another workbook.
Sub ImportApp(ByVal filepath_Report As String, file_name1 As String, wsOutput As Worksheet)
Application.ScreenUpdating = False
Set wbReport = Workbooks.Open(file_name1)
Set wsReport = wbReport.Sheets(1)
Select Case wsOutput.Name
Case Is = "Downtilt Tracker"
wsReport.Activate
With Rows(1)
Set d = .Find("Completed Date")
If d Is Nothing Then
MsgBox "Invalid Down-Tilt Tracker." & vbNewLine & "Please import a valid Down-Tilt Tracker."
wbReport.Close False
Sheets("Control").Activate
Application.ScreenUpdating = True
Exit Sub
End If
End With
End Select
wsReport.Activate: wsReport.Cells.Copy: wsOutput.Activate: Cells(1, 1).Select: ActiveSheet.Paste: Cells.EntireColumn.AutoFit: wsOutput.Range("A1:AB" & 1048576).HorizontalAlignment = xlCenter
wsOutput.Cells(1, 1).Select
Application.CutCopyMode = False
wbReport.Close False
End SubSolution
I like your indentation. It's nice to see well-indented VBA code, it needs to be pointed out!
That's generally a good idea, but all by itself, it makes a poor UX - and if calculation mode remains
Thing is, that's a whole concern all by itself, and as such it deserves its own function. Almost all my macro-enabled Excel workbooks have this function somewhere:
So instead of
The problem with turning
This way you're always sure that all exit paths involve resetting
...which is a good idea, because the next line can actually blow up whenever a bad
I like that: by assigning an object reference, you can work against that object. There are a few issues:
Several things here:
-
You're clearly using the wrong construct here - this should definitely be an
-
I'm actually surprised
And then things get a bit out of hand:
Avoid this - AT ALL COSTS:
There are 7 instructions on that line of code; there's no reason to do this. Ever.
And here's where I'd think your bottleneck is:
You're copying the entire worksheet.
Why bother with the concatenation here? It's hard-coded anyway, and besides,
This might work a bit better - if not, it's at least certainly much easier to read:
Application.ScreenUpdating = FalseThat's generally a good idea, but all by itself, it makes a poor UX - and if calculation mode remains
xlAutomatic, it might be a missed opportunity to gain some more performance by turning off calculations while you're working on the worksheet.Thing is, that's a whole concern all by itself, and as such it deserves its own function. Almost all my macro-enabled Excel workbooks have this function somewhere:
Private Sub ToggleWaitMode(Optional ByVal wait As Boolean = True)
With Excel.Application
.Calculation = IIf(wait, xlCalculationManual, xlCalculationAutomatic)
.Cursor = IIf(wait, xlWait, xlDefault)
.StatusBar = IIf(wait, "Please wait...", False)
.DisplayAlerts = Not wait
.ScreenUpdating = Not wait
End With
End SubSo instead of
Application.ScreenUpdating = False, you can say ToggleWaitMode, and then do ToggleWaitMode False when you're done processing.The problem with turning
ScreenUpdating off, is that if anything goes wrong, it stays off and the user (and sometimes even the dev) is left thinking Excel is frozen: whenever you play with ScreenUpdating, you must handle runtime errors:Private Sub DoSomething()
On Error GoTo CleanFail
'do something
CleanExit:
'clean up code
ToggleWaitMode False
Exit Sub
CleanFail:
'error-handling code
Resume CleanExit
End SubThis way you're always sure that all exit paths involve resetting
ScreenUpdating and calculation mode....which is a good idea, because the next line can actually blow up whenever a bad
file_name1 is passed to the procedure:Set wbReport = Workbooks.Open(file_name1)Set wsReport = wbReport.Sheets(1)I like that: by assigning an object reference, you can work against that object. There are a few issues:
- Where's the declaration? Always use
Option Explicitand declare all variables! If the variable is declared at module level, then the declaration belongs inside the procedure's scope - move it there, to the smallest possible scope.
- The
Sheetscollection contains chart sheets and actual worksheets. You probably intend to query theWorksheetscollection here; if you had declared thewsReportvariableAs Worksheetand theSheets(1)object were actually a chart sheet, you'd have a runtime error here.
Select Case wsOutput.Name
Case Is = "Downtilt Tracker"Several things here:
-
You're clearly using the wrong construct here - this should definitely be an
If...Then block; what's wrong with this?If wsOutput.Name = "Downtilt Tracker" Then
'...
End If-
I'm actually surprised
Case Is = "string literal" actually compiles and works as intended; it's a pretty convoluted way of doing Case "string literal"...And then things get a bit out of hand:
wsReport.Activate
With Rows(1)
Set d = .Find("Completed Date")
If d Is Nothing Then
MsgBox "Invalid Down-Tilt Tracker." & vbNewLine & "Please import a valid Down-Tilt Tracker."
wbReport.Close False
Sheets("Control").Activate
Application.ScreenUpdating = True
Exit Sub
End If
End With- You have a reference to the
wsReportobject - you don't need to.Activateit.
- The
Withblock adds unnecessary indentation and confusion. I'd dowsReport.Rows(1).Findinstead.
dis a meaningless name that doesn't tell anything about what you're doing here.
- You need a reference to the
"Control"sheet. Declare and assign anotherWorksheetvariable, and use it instead ofSelecting andActivateing and using implicit-contextCells.
- If
disn'tNothing, you don't appear to be settingScreenUpdatingback toTrue. The control flow described in my introduction would solve that.
Avoid this - AT ALL COSTS:
wsReport.Activate: wsReport.Cells.Copy: wsOutput.Activate: Cells(1, 1).Select: ActiveSheet.Paste: Cells.EntireColumn.AutoFit: wsOutput.Range("A1:AB" & 1048576).HorizontalAlignment = xlCenterThere are 7 instructions on that line of code; there's no reason to do this. Ever.
And here's where I'd think your bottleneck is:
wsReport.Cells.CopyYou're copying the entire worksheet.
wsOutput.Range("A1:AB" & 1048576)Why bother with the concatenation here? It's hard-coded anyway, and besides,
A1 has its row specified inside the string literal.wsOutput.Range("A1:AB1048576")This might work a bit better - if not, it's at least certainly much easier to read:
wsReport.UsedRange.Copy
wsOutput.Range("A1").Paste
wsOutput.UsedRange.Columns.EntireColumn.AutoFit
wsOutput.UsedRange.HoriontalAlignment = xlCenterCode Snippets
Application.ScreenUpdating = FalsePrivate Sub ToggleWaitMode(Optional ByVal wait As Boolean = True)
With Excel.Application
.Calculation = IIf(wait, xlCalculationManual, xlCalculationAutomatic)
.Cursor = IIf(wait, xlWait, xlDefault)
.StatusBar = IIf(wait, "Please wait...", False)
.DisplayAlerts = Not wait
.ScreenUpdating = Not wait
End With
End SubPrivate Sub DoSomething()
On Error GoTo CleanFail
'do something
CleanExit:
'clean up code
ToggleWaitMode False
Exit Sub
CleanFail:
'error-handling code
Resume CleanExit
End SubSet wbReport = Workbooks.Open(file_name1)Set wsReport = wbReport.Sheets(1)Context
StackExchange Code Review Q#85450, answer score: 4
Revisions (0)
No revisions yet.