ping silently in background - excel

When I execute the following code, a black command window opens and it will flicker until the time all devices pings. How can I run it silently?
Sub PING()
Application.ScreenUpdating = False
Dim strTarget, strPingResult, strInput, wshShell, wshExec
With Sheets(1)
shlastrow = .Cells(Rows.Count, "B").End(xlUp).Row
Set shrange = .Range("B3:B7" & shlastrow)
End With
For Each shCell In shrange
strInput = shCell.Text
If strInput <> "" Then
strTarget = strInput
setwshshell = CreateObject("wscript.shell")
Set wshExec = wshShell.exec("ping -n 2 -w 5 " & strTarget)
strPingResult = LCase(wshExec.stdout.readall)
If InStr(strPingResult, "reply from") Then
shCell.Offset(0, 1).Value = "Reachable"
shCell.Offset(0, 2).Value = "Time"
Else
shCell.Offset(0, 1).Value = "UnReachable"
shCell.Offset(0, 2).Value = "Reachable"
End If
End If
Next shCell
End Sub

Here is the code for that
Sub Do_ping()
With ActiveWorkbook.Worksheets(1)
n = 0
Row = 2
Do
If .Cells(Row, 1) <> "" Then
If IsConnectible(.Cells(Row, 1), 2, 100) = True Then
n = n + 1
Cells(Row, 1).Interior.Color = RGB(0, 255, 0)
Cells(Row, 1).Font.FontStyle = "bold"
Cells(Row, 1).Font.Size = 14
Cells(Row, 2).Interior.Color = RGB(0, 255, 0)
Cells(Row, 2).Value = Time
'Call siren
Else:
n = n + 1
'Cells(Row, 2).Formula = "=NOW()-" & CDbl(Now())
Cells(Row, 1).Interior.Color = RGB(255, 0, 0)
Cells(Row, 3).Value = DateDiff("h:mm:ss", Cells(Row, 2), Now())
End If
End If
Row = Row + 1
Loop Until .Cells(Row, 1) = ""
End With
End Sub
Function IsConnectible(sHost, iPings, iTO)
' Returns True or False based on the output from ping.exe
' Works an "all" WSH versions
' sHost is a hostname or IP
' iPings is number of ping attempts
' iTO is timeout in milliseconds
' if values are set to "", then defaults below used
Dim nRes
If iPings = "" Then iPings = 1 ' default number of pings
If iTO = "" Then iTO = 550 ' default timeout per ping
With CreateObject("WScript.Shell")
nRes = .Run("%comspec% /c ping.exe -n " & iPings & " -w " & iTO _
& " " & sHost & " | find ""TTL="" > nul 2>&1", 0, True)
End With
IsConnectible = (nRes = 0)
End Function

Related

VBA Displaying Cell Reference and Table Range

So i have this userform that allows the user to key in the number of creditors and the number of rows for the table, then after the user clicks confirm, it will generate based on the input values
And now I need this details like which cell contains creditor name 1 and which range is creditor name 1 table like this picture below:
My current code is
'Clears Sheet then generates Number of Creditors & Rows
Worksheets("Payable Conf - by Invoice").Cells.Clear
Dim CreditorsCount As Integer
Dim Counter As Integer
Dim Rows As Integer
If TextBox1.Text <> "" And TextBox2.Text <> "" Then
CreditorsCount = TextBox1.Value
Counter = 0
CreditorsCount2 = 0
Rows = TextBox2.Value
End If
Worksheets("Payable Conf - by Invoice").Activate
While Counter < CreditorsCount
Cells((Counter * (5 + Rows) + 1), 1).Activate
With Range(ActiveCell.Address, ActiveCell.Offset(0, 4))
.Value = Array("Creditor Name " & CStr(Counter + 1), "Creditor Address 1", "Creditor Address 2", "Creditor Address 3", "Staff Email (e.g. abc123#gmail.com)")
.Font.Bold = True
End With
With Range(ActiveCell.Offset(3, 0), ActiveCell.Offset(3, 2))
.Value = Array("Invoice No.", "Invoice Date", "Amount (e.g. $100)")
.Font.Bold = True
End With
With Union(Range(ActiveCell.Address, ActiveCell.Offset(1, 4)), Range(ActiveCell.Offset(3, 0), ActiveCell.Offset(3 + Rows, 2)))
.BorderAround XlLineStyle.xlContinuous, xlThin
.Borders(xlInsideHorizontal).LineStyle = xlContinuous
.Borders(xlInsideVertical).LineStyle = xlContinuous
End With
Counter = Counter + 1
Wend
Worksheets("Payable Conf - by Invoice").Range("I8") = "Please do not edit"
Worksheets("Payable Conf - by Invoice").Range("I9") = "Number of Creditors:"
Worksheets("Payable Conf - by Invoice").Range("J9") = TextBox1.Value
Worksheets("Payable Conf - by Invoice").Range("I10") = "Number of Rows:"
Worksheets("Payable Conf - by Invoice").Range("J10") = TextBox2.Value
Help is greatly appreciated :)
Maybe something like this ?
Sub test()
Dim rg1 As Range
Dim rg2 As Range
Dim cnt As Integer
Dim TotRow As Integer
Dim tbl As Range
cnt = 5
TotRow = 10
With Sheets("Payable Conf - by Invoice")
'.Activate
.Cells.Delete
Set rg1 = .Range("A1")
Set rg2 = .Range("i8")
End With
With rg2
.Resize(3, 1).Value = Application.Transpose(Array("do not edit", "num cred", "num rows"))
.Offset(1, 1).Value = cnt
.Offset(2, 1).Value = TotRow
Set rg2 = rg2.Offset(4, 0)
End With
For i = 1 To cnt
With rg1.Resize(1, 5)
.Value = Array("cr name " & CStr(i), "add1", "add2", "add3", "email")
.Font.Bold = True
.Resize(2, 5).Borders.LineStyle = xlContinuous
End With
Set rg1 = rg1.Offset(3, 0)
With rg1.Resize(1, 3)
.Value = Array("Inv No", "Inv Date", "Inv Amount")
.Font.Bold = True
Set tbl = .Resize(TotRow + 1, 3)
tbl.Borders.LineStyle = xlContinuous
End With
With rg2
.Offset(0, 0).Value = "cred name " & CStr(i) & ":"
.Offset(0, 1).Value = rg1.Offset(-2, 0).Address(0, 0)
.Offset(1, 0).Value = "tbl " & CStr(i) & ":"
.Offset(1, 1).Value = tbl.Address(0, 0)
End With
Set rg1 = rg1.Offset(TotRow + 2, 0)
Set rg2 = rg2.Offset(3, 0)
Next i
End Sub
Please try to run the sub on a new workbook.
If the result is the one that you expected, just change the cnt variable value and the TotRow variable value to your TextBox1.value and TextBox2.value

I keep getting an error that says "Compile error: For without Next"

Hello Everyone. I'm new to VBA and I keep getting an error that says "Compile error:
For without Next"
Sub Aplhabetical_Testing()
Dim ws As Worksheet
Dim ticker As String
Dim vol As Integer
Dim year_open As Double
Dim year_close As Double
Dim yearly_change As Double
Dim percent_change As Double
Dim total_stock_volume As Double
For Each ws In Worksheet
ws.Range("I1").Value = "Ticker"
ws.Range("J1").Value = "Yearly Change"
ws.Range("K1").Value = "Percent Change"
ws.Range("L1").Value = "Total Stock Volume"
ws.Range("P1").Value = "Ticker"
ws.Range("Q1").Value = "Value"
ws.Range("O2").Value = "Greatest % Increase"
ws.Range("O3").Value = "Greatest % Decrease"
ws.Range("O4").Value = "Greatest Total Volume"
For i = 2 To RowCount
j = 0
total = 0
Change = 0
Start = 2
If Cells(i + 1, 1).Value <> Cells(i, 7).Value Then
total = total + Cells(i, 7).Value
Range("I" & 2 + j).Value = Cells(i, 1).Value
Range("j" & 2 + j).Value = 0
Range("K" & 2 + j).Value = "%" & 0
Range("L" & 2 + j).Value = 0
Else
If Cells(Start, 3) = 0 Then
For find_value = Start To i
If Cells(find_value, 3).Value <> 0 Then
Start = find_value
Exit For
End If
Next find_value
End If
Change = (Cells(i, 6) - Cells(Start, 3))
percentChange = Round((Change / Cells(Start, 3) * 100), 2)
Start = i + 1
Range("I" & 2 + j).Value = Cells(i, 1).Value
Range("j" & 2 + j).Value = Round(Change, 2)
Range("K" & 2 + j).Value = "%" & percentChange
Range("L" & 2 + j).Value = total
Select Case Change
Case Is > 0
Range("j" & 2 + j).Interior.ColorIndex = 4
Case Is < 0
Range("j" & 2 + j).Interior.ColorIndex = 3
Case Else
Range("j" & 2 + j).Interior.ColorIndex = 0
End Select
End If
End Sub
There are a number of statements in VBA which must be properly terminated. For instance,
Sub / End Sub,
Function / End Function,
If / End If.
With / End With, or
Enum / End Enum
For better code readability everything between the statement and the End should be indented, like this:-
Sub MySub()
' Here is my code
End Sub
or
If 1 < 2 Then
' Here is what to do in that case
End If
For / Next and Do / Loop work exactly the same way. For example,
For i = 1 to 10
' code to be executed *i* times
Next i
The concepts can be nested. Here's an example.
Private Sub MySub()
Dim i As Integer
For i = 1 to 10
If i = 5 then
Debug.Print "Half done"
End if
Next i
End Sub
You miss two Next:
Sub Aplhabetical_Testing()
Dim ws As Worksheet
Dim ticker As String
Dim vol As Integer
Dim year_open As Double
Dim year_close As Double
Dim yearly_change As Double
Dim percent_change As Double
Dim total_stock_volume As Double
For Each ws In Worksheet ' Worksheets?
ws.Range("I1").Value = "Ticker"
ws.Range("J1").Value = "Yearly Change"
ws.Range("K1").Value = "Percent Change"
ws.Range("L1").Value = "Total Stock Volume"
ws.Range("P1").Value = "Ticker"
ws.Range("Q1").Value = "Value"
ws.Range("O2").Value = "Greatest % Increase"
ws.Range("O3").Value = "Greatest % Decrease"
ws.Range("O4").Value = "Greatest Total Volume"
For i = 2 To RowCount
j = 0
total = 0
Change = 0
Start = 2
If Cells(i + 1, 1).Value <> Cells(i, 7).Value Then
total = total + Cells(i, 7).Value
Range("I" & 2 + j).Value = Cells(i, 1).Value
Range("j" & 2 + j).Value = 0
Range("K" & 2 + j).Value = "%" & 0
Range("L" & 2 + j).Value = 0
Else
If Cells(Start, 3) = 0 Then
For find_value = Start To i
If Cells(find_value, 3).Value <> 0 Then
Start = find_value
Exit For
End If
Next find_value
End If
Change = (Cells(i, 6) - Cells(Start, 3))
percentChange = Round((Change / Cells(Start, 3) * 100), 2)
Start = i + 1
Range("I" & 2 + j).Value = Cells(i, 1).Value
Range("j" & 2 + j).Value = Round(Change, 2)
Range("K" & 2 + j).Value = "%" & percentChange
Range("L" & 2 + j).Value = total
Select Case Change
Case Is > 0
Range("j" & 2 + j).Interior.ColorIndex = 4
Case Is < 0
Range("j" & 2 + j).Interior.ColorIndex = 3
Case Else
Range("j" & 2 + j).Interior.ColorIndex = 0
End Select
End If
' Missing Next
Next
' Missing Next
Next
End Sub

How to merge several cells using VBA

I have some problems with excel and VBA, in that don't know have much knowledge. I copied text from pdf and it's awful.
I have cells which contain some text.
The problem is that the text from one paragraph is broken down over several cells. At the beginning of each paragraph is a word in bold (e.g. CLR.) which describes the rest of the text. As such, it defines where each paragraph should start. How I can merge these cells into one?
I see
I want
Sub MergeText()
Dim strMerged$, r&, j&, i&
r = 1
Do While True
If Cells(r, 1).Characters(1, 1).Font.Bold Then
strMerged = "": strMerged = Cells(r, 1)
r = r + 1
While (Not Cells(r, 1).Characters(1).Font.Bold) And Len(Cells(r, 1)) > 0
strMerged = strMerged & Cells(r, 1)
r = r + 1
Wend
i = i + 1: Cells(i, 2) = strMerged
Cells(i, 2).Characters(1, InStr(1, strMerged, ".", vbTextCompare)).Font.Bold = True
Else
Exit Do
End If
Loop
End Sub
Modify (if needed) and try:
Option Explicit
Sub test()
Dim LastRow As Long, i As Long, j As Long, Count As Long
Dim str As String
With ThisWorkbook.Worksheets("Sheet1") 'Change sheet name if needed
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = LastRow To 2 Step -1
If (UCase(Left(.Range("A" & i), 1)) <> Left(.Range("A" & i), 1)) And UCase(Left(.Range("A" & i - 1), 1)) = Left(.Range("A" & i - 1), 1) Then
Count = 0
For j = 1 To Len(.Range("A" & i - 1))
If .Range("A1").Characters(j, 1).Font.FontStyle = "Bold" Then
Count = Count + 1
Else
Exit For
End If
Next j
str = .Range("A" & i - 1).Value & " " & .Range("A" & i).Value
With .Range("A" & i - 1)
.Value = str
.Font.Bold = False
With .Characters(Start:=1, Length:=Count).Font
.FontStyle = "Bold"
End With
End With
.Rows(i).EntireRow.Delete
ElseIf (UCase(Left(.Range("A" & i), 1)) <> Left(.Range("A" & i), 1)) And UCase(Left(.Range("A" & i - 1), 1)) <> Left(.Range("A" & i - 1), 1) Then
str = .Range("A" & i - 1).Value & " " & .Range("A" & i).Value
With .Range("A" & i - 1)
.Value = str
.Font.Bold = False
End With
.Rows(i).EntireRow.Delete
End If
Next i
End With
End Sub

Vlookup from userform input

I'm trying to build a check in my code, with the user input in a textbox, I'm trying to use a vlookup in previous records in a table to check if that unique value as already been used (initialized).
The target range "erpLots" contains text formatted cells, amd after checking using the VarType() function I know that assigning vValue = SpecEntry.TextBox3.Value vValue is a string type, the error that I'm getting "Type missmatch" is when doing the vlookup If Application.VLookup(vValue, erpLots, 1, False) = SpecEntry.TextBox3.Value Then.
I have a hunch that the error revolves around a type missmatch between the value being searched "vValue" and the target range "erpLots".
Here is the code:
Public intA As Integer
Public foundRow As Double
Sub StartButtonClick()
Dim rowCount As Long
Dim ws As Worksheet
Dim stg As String
Dim erpLots As Range
Dim vValue As Variant
Set erpLots = Worksheets("Inspection Data").Range("C2", Range("C2").End(xlDown))
Set ws = Worksheets("Inspection Data")
rowCount = ws.Range("A111111").End(xlUp).Row
'Checking the userform request info is complete
If Trim(SpecEntry.TextBox1.Value) = vbNullString Then
MsgBox "Please enter Operator ID"
ElseIf Trim(SpecEntry.TextBox2.Value) = vbNullString Then
MsgBox "Please scan or enter spec. number."
ElseIf Trim(SpecEntry.TextBox3.Value) = vbNullString Then
MsgBox "Please scan or enter ERP Lot #."
Else
SpecEntry.TextBox1.Value = UCase(SpecEntry.TextBox1.Value)
SpecEntry.TextBox2.Value = UCase(SpecEntry.TextBox2.Value)
SpecEntry.TextBox3.Value = UCase(SpecEntry.TextBox3.Value)
'checking if ERP Lot # already exist in the list
vValue = SpecEntry.TextBox3.Value
MsgBox "vValue is: " & vValue
If Application.VLookup(vValue, erpLots, 1, False) = SpecEntry.TextBox3.Value Then
foundRow = WorksheetFunction.Match(SpecEntry.TextBox3.Value, erpLots, 1)
Range("G" & foundRow).Value = Now()
Range("H" & foundRow).Value = Range("G" & foundRow).Value - Range("E" & foundRow).Value
Range("H" & foundRow).NumberFormat = "h:mm"
Range("H" & foundRow).Value = Range("H" & foundRow).Value * 1440
Range("H" & foundRow).NumberFormat = "000.00"
intA = 2
ws.Activate
With ws.Cells(ws.Rows.Count, Selection.Column).End(xlUp)
.Select ' not required to change the focus/view
ActiveWindow.ScrollRow = foundRow - 1
End With
Exit Sub
Else
With ws.Range("A1")
intA = 1
.Offset(rowCount, 0).Value = SpecEntry.TextBox1.Value
.Offset(rowCount, 1).Value = SpecEntry.TextBox2.Value
.Offset(rowCount, 2).Value = SpecEntry.TextBox3.Value
.Offset(rowCount, 3).Value = Now()
End With
End If
End If
End Sub
My goal is that if the value exist, the information being captured is to be recorded in the same row but different columns, if the value does not exist, the information would become a new record.
If your Application.match() is working, why not dropthe vloopkup and just:
foundRow = Application.Iferror(WorksheetFunction.Match(SpecEntry.TextBox3.Value, erpLots, 1),0)
Then your If statement is:
If foundRow > 0 Then
Range("G" & foundRow).Value = Now()
Range("H" & foundRow).Value = Range("G" & foundRow).Value - Range("E" & foundRow).Value
Range("H" & foundRow).NumberFormat = "h:mm"
Range("H" & foundRow).Value = Range("H" & foundRow).Value * 1440
Range("H" & foundRow).NumberFormat = "000.00"
intA = 2
ws.Activate
With ws.Cells(ws.Rows.Count, Selection.Column).End(xlUp)
.Select ' not required to change the focus/view
ActiveWindow.ScrollRow = foundRow - 1
End With
Exit Sub
Else
With ws.Range("A1")
intA = 1
.Offset(rowCount, 0).Value = SpecEntry.TextBox1.Value
.Offset(rowCount, 1).Value = SpecEntry.TextBox2.Value
.Offset(rowCount, 2).Value = SpecEntry.TextBox3.Value
.Offset(rowCount, 3).Value = Now()
End With
End If
I went with a countif, as a way to check if the input from the user existed in the target range, and then use that as a condition in the if statement.
Public intA As Integer
Public foundRow As Double
Sub StartButtonClick()
Dim rowCount As Long
Dim ws As Worksheet
Dim stg As String
Dim erpLots As Range
Dim vValue As Variant
Dim count As Integer
Set erpLots = Worksheets("Inspection Data").Range("C2", Range("C2").End(xlDown))
Set ws = Worksheets("Inspection Data")
foundRow = 0
count = 0
rowCount = ws.Range("A111111").End(xlUp).Row
'Checking the userform request info is complete
If Trim(SpecEntry.TextBox1.Value) = vbNullString Then
MsgBox "Please enter Operator ID"
ElseIf Trim(SpecEntry.TextBox2.Value) = vbNullString Then
MsgBox "Please scan or enter spec. number."
ElseIf Trim(SpecEntry.TextBox3.Value) = vbNullString Then
MsgBox "Please scan or enter ERP Lot #."
Else
SpecEntry.TextBox1.Value = UCase(SpecEntry.TextBox1.Value)
SpecEntry.TextBox2.Value = UCase(SpecEntry.TextBox2.Value)
SpecEntry.TextBox3.Value = UCase(SpecEntry.TextBox3.Value)
'checking if ERP Lot # already exist in the list and is coming back from labs
vValue = CStr(Trim(SpecEntry.TextBox3.Value))
count = Application.WorksheetFunction.CountIf(erpLots, vValue)
If count >= 1 Then
foundRow = Application.WorksheetFunction.Match(vValue, erpLots, 0) + 1
MsgBox "row to update is: " & foundRow
Range("G" & foundRow).Value = Now()
Range("G" & foundRow).NumberFormat = "mm/dd/yyyy hh:mm"
Range("H" & foundRow).Value = Range("G" & foundRow).Value - Range("E" & foundRow).Value
Range("H" & foundRow).NumberFormat = "d " & Chr(34) & "days" & Chr(34) & " , h:mm:ss"
intA = 2
ws.Activate
With ws.Cells(ws.Rows.count, Selection.Column).End(xlUp)
.Select ' not required to change the focus/view
ActiveWindow.ScrollRow = foundRow - 1
End With
Exit Sub
Else
With ws.Range("A1")
intA = 1
.Offset(rowCount, 0).NumberFormat = "#"
.Offset(rowCount, 0).Value = SpecEntry.TextBox1.Value
.Offset(rowCount, 1).NumberFormat = "#"
.Offset(rowCount, 1).Value = CStr(SpecEntry.TextBox2.Value)
.Offset(rowCount, 2).NumberFormat = "#"
.Offset(rowCount, 2).Value = CStr(SpecEntry.TextBox3.Value)
.Offset(rowCount, 3).Value = Now()
End With
End If
End If
End Sub

How can I speed this vba code up which involves formatting?

I am setting up a new pricing schedule which reads selected information from a 'Register' tab, based on selected criteria, and copying this into a new tab. This data is formatted so it looks aesthetically pleasing.
I am finding formatting the code is slowing down the run speed significantly. If possible I would like to speed this up as I will be iterating this multiple times.
I hae sped the program up a reasonable amount. Initially it took 30s, whereas now it is about 10s.
I have followed information from this website as best as I can:
https://www.soa.org/News-and-Publications/Newsletters/Compact/2012/january/com-2012-iss42-roper.aspx
I feel there is still scope to improve more, though I am unsure how, and am reaching out to see if there is, or are, better ways to improve the code so it runs quicker.
Option Explicit
Sub create_pricing_schedule()
'define workbook variables
Dim Start_Time As Double, End_Time As Double
Dim file1 As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim ws3 As Worksheet
Dim ws4 As Worksheet
Dim namedRange1 As Range
Dim namedRange2 As Range
Set file1 = ThisWorkbook
Set ws2 = file1.Worksheets("Pricing Schedule")
Set ws3 = file1.Worksheets("Control")
Set ws4 = file1.Worksheets("Register")
Set namedRange1 = file1.Names("Client_Register").RefersToRange
Set namedRange2 = file1.Names("Pricing_Range").RefersToRange
'define general variables
Dim i As Integer
Dim collect(1 To 500, 1 To 10) As Variant
Dim rw As Range
Dim selectedClient As String
Dim lastrow As Integer, lastrow2 As Integer, lastrow3 As Integer
i = 1
'time how long it takes to improve efficiency
Start_Time = Timer
'speedup so less lagg
Call speedup
'delete everything from the pricing schedule/reset
With Sheets("Pricing Schedule")
.UsedRange.ClearContents
.Cells.Interior.ColorIndex = 0
.Cells.Borders.LineStyle = xlNone
.Cells.HorizontalAlignment = xlLeft
.Cells.MergeCells = False
.Range("A:Z").WrapText = False
.Rows.RowHeight = "15"
End With
'resize the client register
lastrow = ws4.Range("A100000").End(xlUp).Row
With ActiveWorkbook.Names("Client_Register")
.RefersTo = "=Register!$A$1:$AE$" & lastrow
End With
selectedClient = ws3.Range("B3").Value
'copy from database to the pricing schedule as a non formatted list of all the info - this runs quickly, but I am open to changing it
For Each rw In Range("Client_Register").Rows
If Range("Client_Register").Cells(rw.Row, 2) = selectedClient Then
collect(i, 1) = Range("Client_Register").Range("E" & rw.Row)
collect(i, 2) = Range("Client_Register").Range("D" & rw.Row)
collect(i, 3) = Range("Client_Register").Range("F" & rw.Row)
collect(i, 4) = Range("Client_Register").Range("J" & rw.Row)
collect(i, 5) = Range("Client_Register").Range("K" & rw.Row)
collect(i, 6) = Range("Client_Register").Range("L" & rw.Row)
collect(i, 7) = Range("Client_Register").Range("M" & rw.Row)
collect(i, 8) = Range("Client_Register").Range("P" & rw.Row)
collect(i, 9) = Range("Client_Register").Range("I" & rw.Row)
collect(i, 10) = Range("Client_Register").Range("H" & rw.Row) ' used to determine if pass through fee
ws2.Range("B" & i + 6) = collect(i, 1)
ws2.Range("C" & i + 6) = collect(i, 2)
ws2.Range("D" & i + 6) = collect(i, 3)
ws2.Range("E" & i + 6) = collect(i, 4)
ws2.Range("F" & i + 6) = collect(i, 5)
ws2.Range("G" & i + 6) = collect(i, 6)
ws2.Range("H" & i + 6) = collect(i, 7)
ws2.Range("I" & i + 6) = collect(i, 8)
ws2.Range("J" & i + 6) = collect(i, 9)
ws2.Range("K" & i + 6) = collect(i, 10)
i = i + 1
End If
Next
'add in the colour and count how many rows there are
lastrow2 = ws2.Range("C5000").End(xlUp).Row
With ActiveWorkbook.Names("Pricing_Range")
.RefersTo = "='Pricing Schedule'!$A$1:$K$" & lastrow2
End With
ws2.Range("B7" & ":" & "J" & lastrow2).Interior.Color = RGB(242, 242, 242)
'==========this bit is slow, can it be quicker?==========
'add spacing, titles, and colour to sub headers
i = 7
For Each rw In Range("Pricing_Range").Rows
If Range("Pricing_Range").Cells(i, 3) <> Range("Pricing_Range").Cells(i + 1, 3) Then
Range("Pricing_Range").Rows(i + 1).Insert Shift:=xlShiftDown
Range("Pricing_Range").Rows(i + 1).Insert Shift:=xlShiftDown
Range("Pricing_Range").Rows(i + 1).Interior.ColorIndex = 0
Range("Pricing_Range").Rows(i + 2).Interior.ColorIndex = 0
Range("Pricing_Range").Range("B" & i + 2 & ":" & "J" & i + 2).Interior.Color = RGB(255, 128, 1)
Range("Pricing_Range").Range("B" & i + 2 & ":" & "J" & i + 2).Borders(xlEdgeTop).Color = RGB(0, 0, 0)
Range("Pricing_Range").Range("B" & i + 2 & ":" & "J" & i + 2).Borders(xlEdgeBottom).Color = RGB(0, 0, 0)
Range("Pricing_Range").Range("B" & i + 2).Value = Range("Pricing_Range").Range("C" & i + 3).Value
'if it is a pass through fee then add it in to the sub headers
If Range("Pricing_Range").Range("K" & i + 3).Value = "Pass-Through" Then
Range("Pricing_Range").Range("J" & i + 2).Value = "Pass-Through Fees"
Range("Pricing_Range").Range("J" & i + 2).HorizontalAlignment = xlRight
End If
i = i + 3
Else
i = i + 1
End If
Next
'==================================================
'set up the main title rows
ws2.Select
Range("Pricing_Range").Range("B2").Value = ws3.Range("B3").Value
Range("Pricing_Range").Range("B2").Font.Size = 20
Range("Pricing_Range").Range("B2").Font.Bold = True
Range("Pricing_Range").Range("B2").Font.FontStyle = "Calibri Light"
Range("Pricing_Range").Range("B2:J3").Select
With Selection
.HorizontalAlignment = xlCenter
.VerticalAlignment = xlCenter
.WrapText = False
.MergeCells = True
.Cells.Interior.Color = RGB(255, 128, 1)
.Cells.Borders(xlEdgeTop).Color = RGB(0, 0, 0)
.Cells.Borders(xlEdgeBottom).Color = RGB(0, 0, 0)
End With
'tidy up things in the sheet
With Worksheets("Pricing Schedule")
'set up the headers and first title
.Range("B6") = .Range("C7")
.Range("B5:J6").Interior.Color = RGB(255, 128, 1)
.Range("B5:J5").Borders(xlEdgeTop).Color = RGB(0, 0, 0)
.Range("B5:J5").Borders(xlEdgeBottom).Color = RGB(0, 0, 0)
.Range("B6:J6").Borders(xlEdgeTop).Color = RGB(0, 0, 0)
.Range("B6:J6").Borders(xlEdgeBottom).Color = RGB(0, 0, 0)
.Range("B5").Value = "Fee Code"
.Range("C5").Value = "Product Line"
.Range("D5").Value = "Item"
.Range("E5").Value = "Volume From"
.Range("F5").Value = "Volume To"
.Range("G5").Value = "Frequency"
.Range("H5").Value = "Location"
.Range("I5").Value = "Price"
.Range("J5").Value = "Nature of Fee"
'tidy up column widths
.Range("A5").RowHeight = 30
.Range("A1").ColumnWidth = 2
.Range("B1").ColumnWidth = 15
.Range("C1").ColumnWidth = 40
.Range("D1").ColumnWidth = 45
.Range("E1").ColumnWidth = 11
.Range("F1").ColumnWidth = 11
.Range("G1").ColumnWidth = 35
.Range("H1").ColumnWidth = 15
.Range("I1").ColumnWidth = 12
.Range("J1").ColumnWidth = 50
.Range("J:J").WrapText = True
.Range("K:K").Delete
End With
'clear the extra orange line at the end
lastrow3 = ws2.Range("B1000").End(xlUp).Row
With ws2.Rows(lastrow3 + 2)
.Cells.Interior.ColorIndex = 0
.Cells.Borders.LineStyle = xlNone
.ClearContents
End With
'add print area
With Worksheets("Pricing Schedule")
.PageSetup.Zoom = False
.PageSetup.Orientation = xlPortrait
.PageSetup.PrintArea = "$B$2:$J$" & lastrow3
.PageSetup.FitToPagesWide = 1
.PageSetup.FitToPagesTall = False
.PageSetup.PrintTitleRows = "$2:$6"
End With
'return to normal
Call slowdown
'time how long it takes to improve efficiency
End_Time = Timer
Worksheets("Control").Cells(6, 2) = End_Time - Start_Time
End Sub
Sub speedup()
Application.Calculation = xlManual
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayStatusBar = False
End Sub
Sub slowdown()
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.DisplayStatusBar = True
End Sub
I found a few lines that could save you some execution time.
'****EDIT****Changed this to direct range reference rather than go through the Names collection.
'Set namedRange1 = file1.Names("Client_Register").RefersToRange
'Set namedRange2 = file1.Names("Pricing_Range").RefersToRange
Set namedRange1 = file1.Range("Client_Register")
Set namedRange2 = file1.Range("Pricing_Range")
Used range takes more time rather use .cells directly
'delete everything from the pricing schedule/reset
'****EDIT***
With ws2 'Sheets("Pricing Schedule")
'used range takes more time rather use .cells directly
.Cells.ClearContents
Rather than use arrays you can directly update values as shown below
'I am using i for the row count
ws2.Range("B" & i + 6).Value = namedRange1.Cells(i, 5).Value
ws2.Range("C" & i + 6).Value = namedRange1.Cells(i, 4).Value
ws2.Range("D" & i + 6).Value = namedRange1.Cells(i, 6).Value
ws2.Range("E" & i + 6).Value = namedRange1.Cells(i, 10).Value
ws2.Range("F" & i + 6).Value = namedRange1.Cells(i, 11).Value
ws2.Range("G" & i + 6).Value = namedRange1.Cells(i, 12).Value
ws2.Range("H" & i + 6).Value = namedRange1.Cells(i, 12).Value
ws2.Range("I" & i + 6).Value = namedRange1.Cells(i, 16).Value
ws2.Range("J" & i + 6).Value = namedRange1.Cells(i, 9).Value
ws2.Range("K" & i + 6).Value = namedRange1.Cells(i, 8).Value
i = i + 1
The main culprit for your slower performance is the insert operation. try to work the logic to not having insert. If not possible, try to insert rows outside the loop in a single operation rather than in the loop
Range("Pricing_Range").Rows(i + 1).Insert Shift:=xlShiftDown
Range("Pricing_Range").Rows(i + 1).Insert Shift:=xlShiftDown
Your handling of the collect array is inefficient. Consider reading the entire Client Register into an array with MyArray = Range.Value. Then prepare the output array in memory and write it to the worksheet after all looping is done, in one go, with TargetRange.Value = collect.
Avoid inserting rows. What's wrong with the existing? If you are preparing all data in an array to be pasted to the worksheet, empty array elements will produce empty worksheet cells. In this way all inserting can be avoided and all you need to do is to format.
There is time cost for every access to the worksheet, whether to read or write. Even for formatting, try to create ranges that are treated in the same manner. Avoid accessing the worksheet in loops.
Example of With and block assignment from an array:
'copy from database to the pricing schedule as a
' non formatted list of all the info - this runs quickly,
' but I am open to changing it
With Range("Client_Register")
For Each rw In .Rows
If .Cells(rw.Row, 2) = selectedClient Then
collect(i, 1) = .Range("E" & rw.Row)
collect(i, 2) = .Range("D" & rw.Row)
collect(i, 3) = .Range("F" & rw.Row)
collect(i, 4) = .Range("J" & rw.Row)
collect(i, 5) = .Range("K" & rw.Row)
collect(i, 6) = .Range("L" & rw.Row)
collect(i, 7) = .Range("M" & rw.Row)
collect(i, 8) = .Range("P" & rw.Row)
collect(i, 9) = .Range("I" & rw.Row)
collect(i, 10) = .Range("H" & rw.Row)
'you could even skip the row-by-row population of values
' and assign as a block after exiting the loop
ws2.Range("B" & i + 6).Resize(1, 10).Value = _
Array(collect(i, 1), collect(i, 2), collect(i, 3), _
collect(i, 4), collect(i, 5), collect(i, 6), _
collect(i, 7), collect(i, 8), collect(i, 9), _
collect(i, 10))
i = i + 1
End If
Next
End With
Note this will break if your Client_Register refers to a range which doesn't start on Row1, because of the relative range references.
Eg:
Range("A1:A10").Range("A1") 'refers to A1
Range("A2:A10").Range("A1") 'refers to A2

Resources