Code to detect hidden rows, unhide them, and apply border - vba

I'm using this bit of VBA code to detect hidden rows on a spreadsheet. On top of this, I want it to unhide those rows and highlight the cells in the previously hidden row from columns A to W with a red border.
Sub ShowRows()
Dim rng As Range
Dim r As Range
Dim sTemp As String
Set rng = Range("A1:A1000")
sTemp = ""
For Each r In rng.Rows
If r.EntireRow.Hidden Then
sTemp = sTemp & "Row " & Mid(r.Address, 4) & vbCrLf
End If
Next r
If sTemp > "" Then
sTemp = "The following rows are hidden:" & vbCrLf & _
vbCrLf & sTemp
MsgBox sTemp
Else
MsgBox "There are no hidden rows."
End If
End Sub
Edit: Sorry. I forgot to mention that a later part of this script applies some conditional formatting to all rows. Whether this part of this script comes before or after that, I suppose it doesn't matter. But I don't want this to take the place of other formatting, just add to it by applying a border.

Something like this?:
Sub ShowRows()
Dim rng As Range
Dim r As Range
Dim sTemp As String
Set rng = Range("A1:A1000")
sTemp = ""
For Each r In rng.Rows
If r.EntireRow.Hidden = True Then
sTemp = sTemp & "Row " & Mid(r.Address, 4) & vbCrLf
r.EntireRow.Hidden = false
With Range("A" & r.Row & ":W" & r.Row).Borders(xlEdgeLeft)
.Color = -16776961
.Weight = xlMedium
End With
With Range("A" & r.Row & ":W" & r.Row).Borders(xlEdgeTop)
.Color = -16776961
.Weight = xlMedium
End With
With Range("A" & r.Row & ":W" & r.Row).Borders(xlEdgeBottom)
.Color = -16776961
.Weight = xlMedium
End With
With Range("A" & r.Row & ":W" & r.Row).Borders(xlEdgeRight)
.Color = -16776961
.Weight = xlMedium
End With
End If
Next r
If sTemp <> "" Then
sTemp = "The following rows are hidden:" & vbCrLf & _
vbCrLf & sTemp
MsgBox sTemp
Else
MsgBox "There are no hidden rows."
End If
End Sub

Just add lines to unhide and colour the rows in your rows loop
Sub ShowRows()
Dim rng As Range
Dim r As Range
Dim sTemp As String
Dim sTemp2 As String
Set rng = Range("A1:A1000")
sTemp = ""
For Each r In rng.Rows
If r.EntireRow.Hidden Then
sTemp = sTemp & "Row " & Mid(r.Address, 4) & vbCrLf
r.Hidden = False
sTemp2 = "A" & Mid(r.Address, 4) & ":H" & Mid(r.Address, 4)
Range(sTemp2).Borders.Color = vbRed
End If
Next r
If sTemp > "" Then
sTemp = "The following rows are hidden:" & vbCrLf & _
vbCrLf & sTemp
MsgBox sTemp
Else
MsgBox "There are no hidden rows."
End If
End Sub

Related

VBA - Creating Word Document from Excel and Edit Certain Line to Contain Bold Text

I am looking to bold every second line entry on a word document that receives input from an excel spreadsheet. In other words, I want the resulting word document to have each line containing 'ID:' to contain bold text. I've looked into other examples but I keep getting errors such as mismatch.
Sub ExceltoWord_TestEnvironment()
Dim wApp As Object
Dim wDoc As Object
Dim strSearchTerm
Dim FirstMatch As Range
Dim FirstAddress
Dim intMyVal As String
Dim lngLastRow As Long
Dim strRowNoList As String
Dim intPlaceHolder As Integer
Set wApp = CreateObject("Word.Application")
Set wDoc = CreateObject("Word.Document")
wApp.Visible = True
Set wDoc = wApp.Documents.Add
wDoc.Range.ParagraphFormat.SpaceBefore = 0
wDoc.Range.ParagraphFormat.SpaceAfter = 0
strSearchTerm = InputBox("Please enter the date to find", "Search criteria")
If strSearchTerm <> "" Then
Set FirstMatch = ActiveSheet.Cells.Find(strSearchTerm, LookAt:=xlPart, MatchCase:=False)
If FirstMatch Is Nothing Then
MsgBox "That date could not be found"
Else
FirstAddress = FirstMatch.Address
intMyVal = strSearchTerm
lngLastRow = Cells(Rows.Count, "F").End(xlUp).Row 'Search Column F, change as required.
For Each cell In Range("F1:F" & lngLastRow) 'F is column
If InStr(1, cell.Value, intMyVal) Then
If strRowNoList = "" Then
strRowNoList = strRowNoList & cell.Row
intPlaceHolder = cell.Row
wDoc.Content.InsertAfter "Group: " & Cells(intPlaceHolder, 3) & vbNewLine
wDoc.Content.InsertAfter "ID: " & Cells(intPlaceHolder, 2) & vbNewLine
wDoc.Content.InsertAfter "Name: " & vbNewLine & vbNewLine
Else
strRowNoList = strRowNoList & ", " & cell.Row
intPlaceHolder = cell.Row
wDoc.Content.InsertAfter "Group: " & Cells(intPlaceHolder, 3) & vbNewLine
wDoc.Content.InsertAfter "ID: " & Cells(intPlaceHolder, 2) & vbNewLine
wDoc.Content.InsertAfter "Name: " & vbNewLine & vbNewLine
End If
Next cell
MsgBox strRowNoList
While Not FirstMatch Is Nothing
Set FirstMatch = ActiveSheet.Cells.FindNext(FirstMatch)
If FirstMatch.Address = FirstAddress Then
Set FirstMatch = Nothing
End If
Wend
End If
End If
End Sub
Example:
Group: Group A
ID: 123456
Name: Jon Snow
Group: Group B
ID: 789101
Name: Samwell Tarly
I was able to find a work around. Thought I would post it here to help others. Sorry my code isn't as clean as I would like it to be. Copying and pasting didn't quite match up.
Sub ExceltoWord_TestEnvironment()
Dim wApp As Object
Dim wDoc As Object
Dim strSearchTerm
Dim FirstMatch As Range
Dim FirstAddress
Dim intMyVal As String
Dim lngLastRow As Long
Dim strRowNoList As String
Dim intPlaceHolder As Integer
Set wApp = CreateObject("Word.Application")
Set wDoc = CreateObject("Word.Document")
wApp.Visible = True
Set wDoc = wApp.Documents.Add
wDoc.Range.ParagraphFormat.SpaceBefore = 0
wDoc.Range.ParagraphFormat.SpaceAfter = 0
strSearchTerm = InputBox("Please enter the date to find", "Search criteria")
If strSearchTerm <> "" Then
Set FirstMatch = ActiveSheet.Cells.Find(strSearchTerm, LookAt:=xlPart, MatchCase:=False)
If FirstMatch Is Nothing Then
MsgBox "That date could not be found"
Else
FirstAddress = FirstMatch.Address
intMyVal = strSearchTerm
lngLastRow = Cells(Rows.Count, "F").End(xlUp).Row 'Search Column F, change as required.
For Each cell In Range("F1:F" & lngLastRow) 'F is column
If InStr(1, cell.Value, intMyVal) Then
If strRowNoList = "" Then
strRowNoList = strRowNoList & cell.Row
intPlaceHolder = cell.Row
intParaCount = wDoc.Paragraphs.Count
i = 2
Set objParagraph = wDoc.Paragraphs(i).Range
With objParagraph
.Font.Bold = True
End With
wDoc.Content.InsertAfter "Group: " & Cells(intPlaceHolder, 3) & vbNewLine
wDoc.Content.InsertAfter "ID: " & Cells(intPlaceHolder, 2) & vbNewLine
wDoc.Content.InsertAfter "Name: " & vbNewLine & vbNewLine
i = i + 4 'paragraph number
Else
strRowNoList = strRowNoList & ", " & cell.Row
intPlaceHolder = cell.Row
wDoc.Content.InsertAfter "Group: " & Cells(intPlaceHolder, 3) & vbNewLine
wDoc.Content.InsertAfter "ID: " & Cells(intPlaceHolder, 2) & vbNewLine
wDoc.Content.InsertAfter "Name: " & vbNewLine & vbNewLine
i = i + 4
End If
Next cell
MsgBox strRowNoList
While Not FirstMatch Is Nothing
Set FirstMatch = ActiveSheet.Cells.FindNext(FirstMatch)
If FirstMatch.Address = FirstAddress Then
Set FirstMatch = Nothing
End If
Wend
End If
End If
End Sub
The code utilizes .paragraphs() where 'i' is the paragraph you want to bold:
i = 2
Set objParagraph = wDoc.Paragraphs(i).Range
With objParagraph
.Font.Bold = True
End With
And the difference in paragraphs is added after each iteration
i = i + 4 'paragraph number

What have I messed up in the VBA loop for each worksheet?

I currently have to send multiple letters out at one time and often replace only 1 or two words within a cell. The problem is that I need those words to be bolded and it would be tedious to use this macro individually on 150 worksheets. I am very new to coding and have tried to search online to edit this code to loop through all of the worksheets, but everything I try seems to only change the current sheet I am on. Below is my current code with what I thought would cause the loop, but instead of looping through the worksheets it seems to only loop through the single worksheet I am on, asking if I would like to bold another word on that sheet.
Origanal code:
Sub FindAndBold()
Dim ws As Worksheet
Dim sFind As String
Dim rCell As Range
Dim rng As Range
Dim lCount As Long
Dim iLen As Integer
Dim iFind As Integer
Dim iStart As Integer
On Error Resume Next
Set rng = ActiveSheet.UsedRange. _
SpecialCells(xlCellTypeConstants, xlTextValues)
On Error GoTo ErrHandler
If rng Is Nothing Then
MsgBox "There are no cells with text"
GoTo ExitHandler
End If
sFind = InputBox( _
Prompt:="What do you want to BOLD?", _
Title:="Text to Bold")
If sFind = "" Then
MsgBox "No text was listed"
GoTo ExitHandler
End If
iLen = Len(sFind)
lCount = 0
For Each rCell In rng
With rCell
iFind = InStr(.Value, sFind)
Do While iFind > 0
.Characters(iFind, iLen).Font.Bold = True
lCount = lCount + 1
iStart = iFind + iLen
iFind = InStr(iStart, .Value, sFind)
Loop
End With
Next
If lCount = 0 Then
MsgBox "There were no occurrences of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "to bold."
ElseIf lCount = 1 Then
MsgBox "One occurrence of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "was made bold."
Else
MsgBox lCount & " occurrences of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "were made bold."
End If
ExitHandler:
Set rCell = Nothing
Set rng = Nothing
Exit Sub
ErrHandler:
MsgBox Err.Description
Resume ExitHandler
End Sub
My most recent attempt:
Sub FindAndBold()
Dim ws As Worksheet
Dim sFind As String
Dim rCell As Range
Dim rng As Range
Dim lCount As Long
Dim iLen As Integer
Dim iFind As Integer
Dim iStart As Integer
For Each ws In ActiveWorkbook.Worksheets
On Error Resume Next
Set rng = ActiveSheet.UsedRange. _
SpecialCells(xlCellTypeConstants, xlTextValues)
On Error GoTo ErrHandler
If rng Is Nothing Then
MsgBox "There are no cells with text"
GoTo ExitHandler
End If
sFind = InputBox( _
Prompt:="What do you want to BOLD?", _
Title:="Text to Bold")
If sFind = "" Then
MsgBox "No text was listed"
GoTo ExitHandler
End If
iLen = Len(sFind)
lCount = 0
For Each rCell In rng
With rCell
iFind = InStr(.Value, sFind)
Do While iFind > 0
.Characters(iFind, iLen).Font.Bold = True
lCount = lCount + 1
iStart = iFind + iLen
iFind = InStr(iStart, .Value, sFind)
Loop
End With
Next
If lCount = 0 Then
MsgBox "There were no occurrences of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "to bold."
ElseIf lCount = 1 Then
MsgBox "One occurrence of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "was made bold."
Else
MsgBox lCount & " occurrences of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "were made bold."
End If
Next ws
ExitHandler:
Set rCell = Nothing
Set rng = Nothing
Exit Sub
ErrHandler:
MsgBox Err.Description
Resume ExitHandler
End Sub
Corrected working code provided provided by YowE3K:
Sub FindAndBold()
Dim ws As Worksheet
Dim sFind As String
Dim rCell As Range
Dim rng As Range
Dim lCount As Long
Dim iLen As Integer
Dim iFind As Integer
Dim iStart As Integer
For Each ws In ActiveWorkbook.Worksheets
Set rng = Nothing
Set rng = ws.UsedRange.SpecialCells(xlCellTypeConstants, xlTextValues)
If rng Is Nothing Then
MsgBox "There are no cells with text"
GoTo ExitHandler
End If
sFind = InputBox( _
Prompt:="What do you want to BOLD?", _
Title:="Text to Bold")
If sFind = "" Then
MsgBox "No text was listed"
GoTo ExitHandler
End If
iLen = Len(sFind)
lCount = 0
For Each rCell In rng
With rCell
iFind = InStr(.Value, sFind)
Do While iFind > 0
.Characters(iFind, iLen).Font.Bold = True
lCount = lCount + 1
iStart = iFind + iLen
iFind = InStr(iStart, .Value, sFind)
Loop
End With
Next
If lCount = 0 Then
MsgBox "There were no occurrences of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "to bold on worksheet '" & ws.Name & "'."
ElseIf lCount = 1 Then
MsgBox "One occurrence of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "was made bold on worksheet '" & ws.Name & "'."
Else
MsgBox lCount & " occurrences of" & _
vbCrLf & "' " & sFind & " '" & _
vbCrLf & "were made bold on worksheet '" & ws.Name & "'."
End If
Next ws
ExitHandler:
Set rCell = Nothing
Set rng = Nothing
Exit Sub
End Sub
You are setting up a loop to go through each worksheet (using ws as your reference to the sheet currently being processed), but then processing a range on the ActiveSheet. Use ws instead of ActiveSheet.
You should also set rng to Nothing before attempting to set it to the UsedRange.SpecialCells or else, if that crashes, your If rng Is Nothing Then statement won't work (because rng will still be set to whatever it was set to on the previous iteration through the loop).
'...
For Each ws In ActiveWorkbook.Worksheets
Set rng = Nothing
On Error Resume Next
Set rng = ws.UsedRange.SpecialCells(xlCellTypeConstants, xlTextValues)
On Error GoTo ErrHandler
If rng Is Nothing Then
'...

How can I Split a Cell by Carriage Return and Append Values?

I'm trying to split a cell by Carriage Return (3 cells to the left of my current cell) and concatenate 'AND' for all Carriage Returns, except the last one, and for the last one I want to concatenate 'YES'
Here is my VBA script.
CellSelect = ActiveCell.Value
CellAddress = ActiveCell.Address
Dim splitVals As Variant
arrLines = Split(Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, -3).Value, Chr(10))
For Each strLine In arrLines
Debug.Print strLine
Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, 0).Value = strLine & Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, -2).Value
Next
End If
Here is a screen shot of my setup. Basically, I'm trying to concatenate what's in the 1st, 2nd, and 3rd cells, into the 4th cell.
I think I'm close. I just can't seem to get it working correctly.
Thanks!!
Just Replace with StrReverse will workfine. No For or Array required.
Sub test()
Dim strOrig As String
Dim strNew As String
'strOrig = Sheet1.Cells(1)
strOrig = "a " & Chr(10) & " b " & Chr(10) & " c " & Chr(10)
Debug.Print strOrig
' a
' b
' c
strNew = StrReverse(Replace(StrReverse(strOrig), Chr(10), StrReverse("YES"), , 1))
strNew = Replace(strNew, Chr(10), "AND")
Debug.Print strNew
'a AND b AND c YES
End Sub
I got it working with this.
CellSelect = ActiveCell.Value
CellAddress = ActiveCell.Address
Dim splitVals As Variant
arrLines = Split(Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, -3).Value, Chr(10))
arrLinesLast = UBound(arrLines)
For Each strLine In arrLines
If arrLinesLast <> 1 Then
If arrLinesLast = 0 Then Exit Sub
Debug.Print strLine
Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, 0).Value = Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, 0).Value & " " & strLine & " " & Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, -2).Value & Chr(10)
arrLinesLast = arrLinesLast - 1
Else
Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, 0).Value = Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, 0).Value & " " & strLine & " " & Sheets("CP (POS) Tasklist").Range(CellAddress).Offset(0, -1).Value
arrLinesLast = arrLinesLast - 1
End If
Next
You can try this: split the cell value to an array and then add AND or YES if it is the last item in the array:
Option Explicit
Sub Test()
Dim rng As Range
Set rng = Sheet1.Range("A1")
AppendAndYes rng
End Sub
Sub AppendAndYes(rngCell As Range)
Dim varItems As Variant
Dim lngIndex As Long
'get lines by splitting on line feed
varItems = Split(rngCell.Value, vbLf, -1, vbBinaryCompare)
'loop through and add AND or YES
For lngIndex = LBound(varItems) To UBound(varItems)
If lngIndex < UBound(varItems) Then
varItems(lngIndex) = varItems(lngIndex) & " AND"
Else
varItems(lngIndex) = varItems(lngIndex) & " YES"
End If
Next lngIndex
'update cell value
rngCell.Value = Join(varItems, vbLf)
End Sub

Match cell value to a combobox row value

I'm trying to figure out a different method of running a piece of code.
Basically what my code is doing at the moment is, looping though column Q in the Global sheet, then looping though Combobox2, when it finds a match the entire rows get moved to the sheet reference in column 1 of the combobox.
Is it possible to use the Match function to achieve the same results and speed up the code??
This is currently the code I'm using, it does what I need it to do, but I cannot get error handling working for it. And it there are many rows of data to loop through it can take a long time!
Option 1:
Private Sub CommandButton6_Click()
Dim i As Long, j As Long, lastG As Long, strWS As String, rngCPY As Range
Dim StartTime As Double
Dim SecondsElapsed As Double
With Application
.ScreenUpdating = False
.EnableEvents = False
.CutCopyMode = False
End With
StartTime = Timer
If Range("L9") = "" Then
MsgBox "You can't Split the Jobs at this stage. " & vbLf & vbLf & "Please create the form for the Sub-Contractor First." & vbLf & vbLf & "Please press Display Utilities to create form.", vbExclamation, "Invalid Operation"
Exit Sub
End If
If sheets("Global").Range("A3") = "" Then
MsgBox "The appears to be no application loaded." & vbLf & vbLf & "Please load" & " " & Range("C11") & " " & "App and Planet Info, then click button 2 and try again.", vbExclamation, "Invalid Operation"
Exit Sub
End If
On Error GoTo bm_Close_Out
' find last row
lastG = sheets("Global").Cells(Rows.Count, "Q").End(xlUp).row
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
If sheets("PAYMENT FORM").Range("L40") >= 1 Then
MsgBox "It appears you have already split the jobs, this operation can only be performed once.", vbExclamation, "Invalid Operation"
Exit Sub
Else
For j = 0 To Me.ComboBox2.ListCount - 1
currval = Me.ComboBox2.List(j, 0) ' value to match
For i = 3 To lastG
lookupVal = sheets("Global").Cells(i, "Q") ' value to find
If lookupVal = currval Then
Set rngCPY = sheets("Global").Cells(i, "Q").EntireRow
strWS = Me.ComboBox2.List(j, 1)
On Error GoTo bm_Need_Worksheet '<~~ if the worksheet in the next line does not exist, go make one
With Worksheets(strWS)
rngCPY.Copy
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
End If
Next i
Next j
End If
Else
If sheets("PAYMENT FORM").Range("L35") >= 1 Then
MsgBox "It appears you have already split the jobs, this operation can only be performed once.", vbExclamation, "Invalid Operation"
Exit Sub
Else
For j = 0 To Me.ComboBox2.ListCount - 1
currval = Me.ComboBox2.List(j, 0) ' value to match
For i = 3 To lastG
lookupVal = sheets("Global").Cells(i, "Q") ' value to find
If lookupVal = currval Then
Set rngCPY = sheets("Global").Cells(i, "Q").EntireRow
strWS = Me.ComboBox2.List(j, 1)
On Error GoTo bm_Need_Worksheet '<~~ if the worksheet in the next line does not exist, go make one
With Worksheets(strWS)
rngCPY.Copy
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
End If
Next i
Next j
End If
End If
GoTo bm_Close_Out
bm_Need_Worksheet:
On Error GoTo 0
With Worksheet
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsTemplate As Worksheet: Set wsTemplate = wb.sheets("Template")
Dim wsPayment As Worksheet: Set wsPayment = wb.sheets("Payment Form")
Dim wsNew As Worksheet
Dim lastRow2 As Long
Dim Contract As String: Contract = sheets("Payment Form").Range("C9").value
Dim SpacePos As Integer: SpacePos = InStr(Contract, "- ")
Dim Name As String: Name = Left(Contract, SpacePos)
Dim Name2 As String: Name2 = Right(Contract, Len(Contract) - Len(Name))
Dim NewName As String: NewName = strWS
Dim CCName As Variant: CCName = Me.ComboBox2.List(j, 2)
Dim LastRow As Long: LastRow = wsPayment.Range("U36:U53").End(xlDown).row
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
lastRow2 = wsPayment.Range("A23:A39").End(xlDown).row
Else
lastRow2 = wsPayment.Range("A18:A34").End(xlDown).row
End If
wsTemplate.Visible = True
wsTemplate.Copy before:=sheets("Details"): Set wsNew = ActiveSheet
wsTemplate.Visible = False
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
With wsPayment
For Each cell In .Range("A23:A39")
If Len(cell) = 0 Then
If sheets("Payment Form").Range("C9").value = "Network" Then
cell.value = NewName & " - " & Name2 & ": " & CCName
Else
cell.value = NewName & " -" & Name2 & ": " & CCName
End If
Exit For
End If
Next cell
End With
Else
With wsPayment
For Each cell In .Range("A18:A34")
If Len(cell) = 0 Then
If sheets("Payment Form").Range("C9").value = "Network" Then
cell.value = NewName & " - " & Name2 & ": " & CCName
Else
cell.value = NewName & " -" & Name2 & ": " & CCName
End If
Exit For
End If
Next cell
End With
End If
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
With wsNew
.Name = NewName
.Range("D4").value = wsPayment.Range("A23:A39").End(xlDown).value
.Range("D6").value = wsPayment.Range("L11").value
.Range("D8").value = wsPayment.Range("C9").value
.Range("D10").value = wsPayment.Range("C11").value
End With
Else
With wsNew
.Name = NewName
.Range("D4").value = wsPayment.Range("A18:A34").End(xlDown).value
.Range("D6").value = wsPayment.Range("L11").value
.Range("D8").value = wsPayment.Range("C9").value
.Range("D10").value = wsPayment.Range("C11").value
End With
End If
wsPayment.Activate
With wsPayment
.Range("J" & lastRow2 + 1).value = 0
.Range("L" & lastRow2 + 1).Formula = "=N" & lastRow2 + 1 & "-J" & lastRow2 + 1 & ""
.Range("N" & lastRow2 + 1).Formula = "='" & NewName & "'!L20"
.Range("U" & LastRow + 1).value = NewName & ": "
.Range("V" & LastRow + 1).Formula = "='" & NewName & "'!I21"
.Range("W" & LastRow + 1).Formula = "='" & NewName & "'!I23"
.Range("X" & LastRow + 1).Formula = "='" & NewName & "'!K21"
End With
End With
On Error GoTo bm_Close_Out
Resume
bm_Close_Out:
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
With Application
.ScreenUpdating = True
.EnableEvents = True
.CutCopyMode = True
End With
End Sub
Option 2:
Private Sub CommandButton1_Click()
Dim j As Long, strWS As String, rngCPY As Range, FirstAddress As String, sSheetsWithData As String
Dim sSheetsWithoutData As String, lSheetRowsCopied As Long, lAllRowsCopied As Long, bFound As Boolean, sOutput As String
Dim StartTime As Double
Dim SecondsElapsed As Double
With Application
.ScreenUpdating = False
.EnableEvents = False
.CutCopyMode = False
.EnableEvents = False
End With
StartTime = Timer
On Error GoTo bm_Close_Out
For j = 0 To UserForm2.ComboBox2.ListCount - 1
bFound = False
currval = UserForm2.ComboBox2.List(j, 0) ' value to match
With sheets("Global")
Set rngCPY = sheets("Global").Range("Q:Q").Find(currval, LookIn:=xlValues)
If Not rngCPY Is Nothing Then
bFound = True
lSheetRowsCopied = 0
FirstAddress = rngCPY.Address
Do
lSheetRowsCopied = lSheetRowsCopied + 1
strWS = UserForm2.ComboBox2.List(j, 1)
On Error GoTo bm_Need_Worksheet
With Worksheets(strWS)
rngCPY.EntireRow.Copy
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
Set rngCPY = sheets("Global").Range("Q:Q").FindNext(rngCPY)
Loop Until rngCPY Is Nothing Or rngCPY.Address = FirstAddress
Else
bFound = False
End If
If bFound Then
sSheetsWithData = sSheetsWithData & " " & strWS & " (" & lSheetRowsCopied & ")" & vbLf
lAllRowsCopied = lAllRowsCopied + lSheetRowsCopied
End If
End With
Next j
bm_Need_Worksheet:
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsTemplate As Worksheet: Set wsTemplate = wb.sheets("Template")
Dim wsPayment As Worksheet: Set wsPayment = wb.sheets("Payment Form")
Dim wsNew As Worksheet
Dim lastRow2 As Long
Dim Contract As String: Contract = sheets("Payment Form").Range("C9").value
Dim SpacePos As Integer: SpacePos = InStr(Contract, "- ")
Dim Name As String: Name = Left(Contract, SpacePos)
Dim Name2 As String: Name2 = Right(Contract, Len(Contract) - Len(Name))
Dim NewName As String: NewName = strWS
Dim CCName As Variant: CCName = UserForm2.ComboBox2.List(j, 2)
Dim LastRow As Long: LastRow = wsPayment.Range("U36:U53").End(xlDown).row
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
lastRow2 = wsPayment.Range("A23:A39").End(xlDown).row
Else
lastRow2 = wsPayment.Range("A18:A34").End(xlDown).row
End If
wsTemplate.Visible = True
wsTemplate.Copy before:=sheets("Details"): Set wsNew = ActiveSheet
wsTemplate.Visible = False
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
With wsPayment
For Each cell In .Range("A23:A39")
If Len(cell) = 0 Then
If sheets("Payment Form").Range("C9").value = "Network" Then
cell.value = NewName & " - " & Name2 & ": " & CCName
Else
cell.value = NewName & " -" & Name2 & ": " & CCName
End If
Exit For
End If
Next cell
End With
Else
With wsPayment
For Each cell In .Range("A18:A34")
If Len(cell) = 0 Then
If sheets("Payment Form").Range("C9").value = "Network" Then
cell.value = NewName & " - " & Name2 & ": " & CCName
Else
cell.value = NewName & " -" & Name2 & ": " & CCName
End If
Exit For
End If
Next cell
End With
End If
If InStr(1, sheets("Payment Form").Range("A20").value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
With wsNew
.Name = NewName
.Range("D4").value = wsPayment.Range("A23:A39").End(xlDown).value
.Range("D6").value = wsPayment.Range("L11").value
.Range("D8").value = wsPayment.Range("C9").value
.Range("D10").value = wsPayment.Range("C11").value
End With
Else
With wsNew
.Name = NewName
.Range("D4").value = wsPayment.Range("A18:A34").End(xlDown).value
.Range("D6").value = wsPayment.Range("L11").value
.Range("D8").value = wsPayment.Range("C9").value
.Range("D10").value = wsPayment.Range("C11").value
End With
End If
wsPayment.Activate
With wsPayment
.Range("J" & lastRow2 + 1).value = 0
.Range("L" & lastRow2 + 1).Formula = "=N" & lastRow2 + 1 & "-J" & lastRow2 + 1 & ""
.Range("N" & lastRow2 + 1).Formula = "='" & NewName & "'!L20"
.Range("U" & LastRow + 1).value = NewName & ": "
.Range("V" & LastRow + 1).Formula = "='" & NewName & "'!I21"
.Range("W" & LastRow + 1).Formula = "='" & NewName & "'!I23"
.Range("X" & LastRow + 1).Formula = "='" & NewName & "'!K21"
End With
On Error GoTo bm_Close_Out
Resume
bm_Close_Out:
If sSheetsWithData <> vbNullString Then
sOutput = "# of rows copied to sheets:" & vbLf & vbLf & sSheetsWithData & vbLf & _
"Total rows copied = " & lAllRowsCopied & vbLf & vbLf
Else
sOutput = "No sheets contained data to be copied" & vbLf & vbLf
End If
If sSheetsWithoutData <> vbNullString Then
sOutput = sOutput & "Sheets with no rows copied:" & vbLf & vbLf & sSheetsWithoutData
Else
sOutput = sOutput & "All sheets had data that was copied."
End If
If sOutput <> vbNullString Then MsgBox sOutput, , "Copy Report"
Set rngCPY = Nothing
SecondsElapsed = Round(Timer - StartTime, 2)
MsgBox "This code ran successfully in " & SecondsElapsed & " seconds", vbInformation
With Application
.ScreenUpdating = True
.EnableEvents = True
.CutCopyMode = True
.EnableEvents = True
End With
End Sub
OK... It's more like a try than an answer. pls check if that is working and if it is faster.
Use this macro only with a copy of your workbook!
Private Sub CommandButton2_Click()
Dim i As Long, j As Long, k As Long, strWS As String, rngCPY As Range
Dim noFind As Variant: noFind = UserForm2.ComboBox2.List '<~~~ get missed items
With Application: .ScreenUpdating = False: .EnableEvents = False: .CutCopyMode = False: End With
If Range("L9") = "" Then: MsgBox "You can't Split the Jobs at this stage. " & vbLf & vbLf & "Please create the form for the Sub-Contractor First." & vbLf & vbLf & "Please press Display Utilities to create form.", vbExclamation, "Invalid Operation": Exit Sub
Dim lastG As Long: lastG = Sheets("Global").Cells(Rows.Count, 17).End(xlUp).row
Dim cVat As Boolean: cVat = InStr(1, Sheets("Payment Form").Range("A20").Value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE")
If Sheets("PAYMENT FORM").Cells(35 - cVat * 5, 12) >= 1 Then: MsgBox "It appears you have already split the jobs, this operation can only be performed once.", vbExclamation, "Invalid Operation": Exit Sub
'~~~ acivate next line to sort (will speed up a lot)
'Sheets("Global").Range("A3:R" & Cells(Rows.Count, 17).End(xlUp).row).Sort cells(3,17), 1
For j = 0 To UserForm2.ComboBox2.ListCount - 1
noFind(j, 4) = 0
For i = 3 To lastG
If noFind(j, 0) = Sheets("Global").Cells(i, 17) Then
k = i
strWS = UserForm2.ComboBox2.List(j, 1)
On Error Resume Next
If Len(Worksheets(strWS).Name) = 0 Then
With ThisWorkbook
On Error GoTo 0
Dim nStr As String: With Sheets("Payment Form").Range("C9"): nStr = Right(.Value, Len(.Value) - Len(Left(.Value, InStr(.Value, "- ")))): End With
Dim CCName As Variant: CCName = UserForm2.ComboBox2.List(j, 2)
Dim lastRow As Long: lastRow = Sheets("Payment Form").Range("U36:U53").End(xlDown).row + 1
Dim strRng As String: strRng = Array("A18:A34", "A23:A39")(-1 * cVat)
Dim lastRow2 As Long: lastRow2 = Sheets("Payment Form").Range(strRng).End(xlDown).row + 1
Dim wsNew As Worksheet: .Sheets("Template").Copy .Sheets(.Sheets.Count): Set wsNew = .Sheets(.Sheets.Count): wsNew.Move .Sheets("Details")
With Sheets("Payment Form")
For Each cell In .Range(strRng)
If Len(cell) = 0 Then
If Sheets("Payment Form").Range("C9").Value = "Network" Then
cell.Offset.Value = strWS & " - " & nStr & ": " & CCName
Else
cell.Offset.Value = strWS & " -" & nStr & ": " & CCName
End If
Exit For
End If
Next cell
End With
With wsNew
.Visible = -1
.Name = strWS
.Cells(4, 4).Value = Sheets("Payment Form").Range(strRng).End(xlDown).Value
.Cells(6, 4).Value = Sheets("Payment Form").Cells(12, 12).Value
.Cells(8, 4).Value = Sheets("Payment Form").Cells(9, 3).Value
.Cells(10, 4).Value = Sheets("Payment Form").Cells(11, 3).Value
End With
With .Sheets("Payment Form")
.Activate
.Cells(lastRow2, 10).Value = 0
.Cells(lastRow2, 12).Formula = "=N" & lastRow2 & "-J" & lastRow2 & ""
.Cells(lastRow2, 14).Formula = "='" & strWS & "'!L20"
.Cells(lastRow, 21).Value = strWS & ": "
.Cells(lastRow, 22).Formula = "='" & strWS & "'!I21"
.Cells(lastRow, 23).Formula = "='" & strWS & "'!I23"
.Cells(lastRow, 24).Formula = "='" & strWS & "'!K21"
End With
End With
End If
On Error GoTo 0
While Sheets("Global").Cells(k + 1, 17).Value = noFind(j, 0) And k < lastG
k = k + 1
Wend
Set rngCPY = Sheets("Global").Range("Q" & i & ":Q" & k).EntireRow
With Worksheets(strWS)
rngCPY.Copy
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
noFind(j, 4) = noFind(j, 4) + k - i + 1
i = k
End If
Next i
Next j
With Application: .ScreenUpdating = True: .EnableEvents = True: .CutCopyMode = True: End With
'noFind(x, y) > x = item / y: 0 = name / y: 4 = counter
noFind(0, 0) = noFind(0, 0) & " " & noFind(0, 4) & " times copied"
For i = 1 To UBound(noFind)
noFind(0, 0) = noFind(0, 0) & vbLf & noFind(i, 0) & " " & noFind(i, 4) & " times copied"
Next
MsgBox noFind(0, 0)
End Sub
At first: you may add some empty lines for better understanding...
Most parts are just shortened by view (they still do tha same).
When using the sort option, it will copy/paste all rows for each keyword in one step. That not only sounds faster... However, you may resort at the end again
Pls check if it works with your real workbook (copy of it, but with all data inside). I haven't done any "indeep speed tuning".
Here is a small section of your code that replace the loop through each cell in Global!Q3:Q*<last_row>* with the VBA version of the MATCH function.
Dim rw As Long, rngGQs As Range '<~~ put this closer to the top with the other variable declarations
' find last row
'lastG = Sheets("Global").Cells(Rows.Count, "Q").End(xlUp).Row '<~~old way
With Sheets("Global") '<~~new way
Set rngGQs = .Range(Cells(3, "Q"), .Cells(Rows.Count, "Q").End(xlUp)) '< ~~ all of the cells to look at
End With
If InStr(1, Sheets("Payment Form").Range("A20").Value, "THE VAT SHOWN IS YOUR OUTPUT TAX DUE TO CUSTOMS AND EXCISE") > 0 Then
If Sheets("PAYMENT FORM").Range("L40") >= 1 Then
MsgBox "It appears you have already split the jobs, this operation can only be performed once.", vbExclamation, "Invalid Operation"
Exit Sub
Else
For j = 0 To Me.ComboBox2.ListCount - 1
currval = Me.ComboBox2.List(j, 0) ' value to match
'For i = 3 To lastG '<~~old way
'lookupVal = Sheets("Global").Cells(i, "Q") ' value to find
'If lookupVal = currval Then
If Not IsError(Application.Match(currval, rngGQs, 0)) Then '<~~new way
rw = Application.Match(currval, rngGQs, 0)
Set rngCPY = Sheets("Global").Cells(rw, "Q").EntireRow
'all the rest here
When you get this to a satisfactory working order, it will be a prime candidate for suggestions at Code Review (Excel).
You could try something like this. The Range.Find-Method basically looks through the given range for a value which you can specify. If a match is found, the cell in which the match is found, can then be stored.
You can then also use .FindNext to find the next occurrence of that value, if needed.
For j = 0 To Me.ComboBox2.ListCount - 1
currval = Me.ComboBox2.List(j, 0) ' value to match
Set rngCPY = sheets("Global").Range("Q:Q").Find(currval, LookIn:=xlValues)
Do While Not rngCPY Is Nothing
strWs = Me.ComboBox2.List(j, 1)
rngCPY.EntireRow.Copy
With Worksheets(strWS)
.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Insert shift:=xlDown
End With
Set rngCPY = sheets("Global").Range("Q:Q").FindNext(rngCPY)
Loop
Next j

Saving after moving values from left to right using list boxes

Please help me for the list boxes.
What I am trying to do is:
I selected two items from list box and moved them to right.
I saved Excel file. I reopened the file and nothing was present in the right.
Please help. I have used following code in VBA:
Private Sub CommandButton6_Click()
Dim iCtr As Long
For iCtr = 0 To Me.ListBox2.ListCount - 1
Me.ListBox1.AddItem Me.ListBox2.List(iCtr)
Next iCtr
Me.ListBox2.Clear
End Sub
Private Sub BTN_moveAllRight_Click()
Dim iCtr As Long
For iCtr = 0 To Me.ListBox1.ListCount - 1
Me.ListBox2.AddItem Me.ListBox1.List(iCtr)
Next iCtr
Me.ListBox1.Clear
End Sub
Private Sub BTN_MoveSelectedLeft_Click()
Dim iCtr As Long
For iCtr = 0 To Me.ListBox2.ListCount - 1
If Me.ListBox2.Selected(iCtr) = True Then
Me.ListBox1.AddItem Me.ListBox2.List(iCtr)
End If
Next iCtr
For iCtr = Me.ListBox2.ListCount - 1 To 0 Step -1
If Me.ListBox2.Selected(iCtr) = True Then
Me.ListBox2.RemoveItem iCtr
End If
Next iCtr
End Sub
Private Sub BTN_MoveSelectedRight_Click()
Dim iCtr As Long
For iCtr = 0 To Me.ListBox1.ListCount - 1
If Me.ListBox1.Selected(iCtr) = True Then
Me.ListBox2.AddItem Me.ListBox1.List(iCtr)
End If
Next iCtr
For iCtr = Me.ListBox1.ListCount - 1 To 0 Step -1
If Me.ListBox1.Selected(iCtr) = True Then
Me.ListBox1.RemoveItem iCtr
End If
Next iCtr
End Sub
Private Sub cmdOK_Click()
Unload Me
End Sub
Private Sub Worksheet_Activate()
Dim myCell As Range
Dim rngItems As Range
Set rngItems = Sheets("Subject Disposition").Range("Route")
Me.ListBox1.Clear
Me.ListBox2.Clear
With Me.ListBox1
.LinkedCell = ""
.ListFillRange = ""
For Each myCell In rngItems.Cells
If Trim(myCell) <> "" Then
.AddItem myCell.Value
End If
Next myCell
End With
Me.ListBox1.MultiSelect = fmMultiSelectMulti
Me.ListBox2.MultiSelect = fmMultiSelectMulti
End Sub
I made a sample for you.
First set up the Source Sheet (in this sample, we use Sheet1) and the UserForm like what you see below:
As you can see, we have the initial data or list in Cell A1:A10 in Sheet1.
To display it in the UserForm you created, you use RowSource property as seen in UserForm_Initialize event as what David pointed out. (See below)
And then you see the codes for the rest of the buttons which moves items selected from left to right and vise versa.
Also the move all to left or right button.
Basically, what we're doing is manipulating Range Objects in Sheet1 and then updates the RowSource property at the end of each code block to make it look like we are manipulating the Listboxes.
Now when you save the worksheet, It will retain whatever value Ranges A1:A10 and B1:B10 have. HTH
Option Explicit
Private Sub CommandButton1_Click() 'move item right to left
Dim rng As Range
Dim i As Long, j As Long
With Me.ListBox2 'right listbox
For i = 0 To .ListCount - 1
If .Selected(i) = True Then
Set rng = Sheet1.Range("B1:B10").Find(.List(i), [B10])
If Not rng Is Nothing Then
With Sheet1
If Len(.Range("A1").Value) = 0 Then
j = 1
Else
j = .Range("A" & .Rows.Count).End(xlUp).Offset(1, 0).Row
End If
rng.Copy .Range("A" & j)
rng.Delete xlUp
End With
End If
End If
Next
End With
DoEvents
Me.ListBox1.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "A1:A10"
Me.ListBox2.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "B1:B10"
End Sub
Private Sub CommandButton2_Click() 'move item left to right
Dim rng As Range
Dim i As Long, j As Long
With Me.ListBox1 'left listbox
For i = 0 To .ListCount - 1
If .Selected(i) = True Then
Set rng = Sheet1.Range("A1:A10").Find(.List(i), [A10])
If Not rng Is Nothing Then
With Sheet1
If Len(.Range("B1").Value) = 0 Then
j = 1
Else
j = .Range("B" & .Rows.Count).End(xlUp).Offset(1, 0).Row
End If
rng.Copy .Range("B" & j)
rng.Delete xlUp
End With
End If
End If
Next
End With
DoEvents
Me.ListBox1.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "A1:A10"
Me.ListBox2.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "B1:B10"
End Sub
Private Sub CommandButton3_Click() 'move all to left
Dim rng As Range
With Sheet1
If Me.ListBox2.ListCount = 0 Then Exit Sub
Set rng = .Range("B1", .Range("B" & .Rows.Count).End(xlUp))
If Len(.Range("A1").Value) = 0 Then
rng.Copy .Range("A1")
Else
rng.Copy .Range("A" & .Rows.Count).End(xlUp).Offset(1, 0)
End If
rng.ClearContents
End With
DoEvents
Me.ListBox1.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "A1:A10"
Me.ListBox2.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "B1:B10"
End Sub
Private Sub CommandButton4_Click() 'move all to right
Dim rng As Range
With Sheet1
If Me.ListBox1.ListCount = 0 Then Exit Sub
Set rng = .Range("A1", .Range("A" & .Rows.Count).End(xlUp))
If Len(.Range("B1").Value) = 0 Then
rng.Copy .Range("B1")
Else
rng.Copy .Range("B" & .Rows.Count).End(xlUp).Offset(1, 0)
End If
rng.ClearContents
End With
DoEvents
Me.ListBox1.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "A1:A10"
Me.ListBox2.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "B1:B10"
End Sub
Private Sub UserForm_Initialize()
'Initialize the left and right listbox value
Me.ListBox1.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "A1:A10"
Me.ListBox2.RowSource = _
"'[" & ThisWorkbook.Name & "]" & Sheet1.Name & "'!" & "B1:B10"
End Sub