I have a userform of 7 checkboxes and some labels that describe them. For each corresponding checkbox there is an array from which there will be created a report if the checkbox is checked as true. However, it doesn't loop through correctly.
I want it to loop through as A, B, C, D, E, F, G for for each checkbox who have the TabIndex numbers of 0, 1, 2, 3, 4, 5, 6 respectively. However it loops through in the order of 0,6,5,4,3,2,1.
I have a main sub that defines and declares variables. My userform print code is as follows:
Sub Get_PDF_Click()
' Creating PDF
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
PDFUserForm.Hide
i = 0
j = 0
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
If ctl.Value = True Then
j = j + 1
Name_of_File = Array(i + 1, 1) & " report" & YYMM & ".xlsx"
Workbooks.Open Filename:=OutputPath & Name_of_File
Set Wkb = Workbooks(Name_of_File)
For Each ws In Wkb.Worksheets
PDF_Name = Array(i + 1, 1) & " " & ws.Name & " " & YYMM
ws.ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
OutputPath & PDF_Name, Quality _
:=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas _
:=False, OpenAfterPublish:=False
Next ws
Wkb.Close SaveChanges:=False
End If ' See if checked
i = i + 1
Debug.Print ctl.Name
End If ' See if checkbox
Next ctl
If j > 0 Then
' Notification on process time
SecondsElapsed = Round(Timer - StartTime, 0)
MsgBox "PDF succesfully published after " & SecondsElapsed & " seconds." & Chr(10) & "Location: " & OutputPath, vbInformation
Else
MsgBox "No file was selected.", vbInformation
End If
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
As an aside I have a similar problem in another piece of code where I loop through charts on a worksheet which is also looping in the wrong order, so perhaps the same solution concept can be applied to that.
For Each isn't specified to guarantee an enumeration order. In all likelihood the controls are being enumerated in the order they were added to the Me.Controls collection.
If you need a specific order, use a For loop:
Dim checkboxNames As Variant
checkboxNames = Array("chkA", "chkB", "chkC", "chkD", "chkE", ...)
Dim current As Long, checkboxName As String, currentBox As MSForms.CheckBox
For current = LBound(checkboxNames) To UBound(checkboxNames)
checkboxName = checkboxNames(current)
Set currentBox = Me.Controls(checkboxName)
'work with the currentBox here
Next
Note that this also removes the need to iterate controls you're not interested in
Here is another way ;) This does not need you to hard code the name of the checkboxes.
Logic: Create a 2D array. Store Tabindex and CheckBox Name in the array. Sort it on Tabindex and use it as you want it :)
Code:
Option Explicit
Private Sub Sample()
Dim CbArray() As String
Dim n As Long: n = 1
Dim cbCount As Long
Dim tindex As String, ctlname As String
Dim ctl As Control
Dim i As Long, j As Long
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
n = n + 1
End If
Next
n = n - 1: cbCount = n
ReDim CbArray(1 To n, 1 To 2)
n = 1
'~~> Sort the Tabindex and checkbox name in the array
For Each ctl In Me.Controls
If TypeName(ctl) = "CheckBox" Then
CbArray(n, 1) = ctl.TabIndex
CbArray(n, 2) = ctl.Name
n = n + 1
End If
Next
'~~> Sort the array
For i = 1 To cbCount
For j = i + 1 To cbCount
If CbArray(i, 1) < CbArray(j, 1) Then
tindex = CbArray(j, 1)
ctlname = CbArray(j, 2)
CbArray(j, 1) = CbArray(i, 1)
CbArray(j, 2) = CbArray(i, 2)
CbArray(i, 1) = tindex
CbArray(i, 2) = ctlname
End If
Next j
Next i
'~~> Loop through the checkboxes
For i = cbCount To 1 Step -1
With Controls(CbArray(i, 2))
Debug.Print .Name
'
'~~> Do what you want
'
End With
Next i
End Sub
Related
Kindly see below code where it takes too much time run for more than 30rows in a range. (its similar to knapsack algorithm requirements)
let me try to explain below in detail,
Input Base sheet: Column A having values (For ex: 1555),
Column B having its Assignment value (A1),
Column C & D its filter value which will perform against input data sheet file.
Program working concept:
it takes first row(2) data from base sheet and apply filter (C2 & D2 value) in input data sheet (Column A & B respectively) then it checks value in column C and it find best sum to match the value (1555) or nearest to it and after it assigns value (which is A1) against those rows and repeats the same for next rows.
I have posted image below.
Kindly refer for Input Base sheet and Input Data sheet and
copy the codes in another workbook.
Run the macro, Choose Base sheet and the Data sheet. Program would run and assigns in Input data sheet. It runs super fast in lesser rows when I have more rows it gets hang/takes too hours to run.
Help me to where it can be speed up.
Appreciate your supports.
Thanks
input base sheet
input data sheet:
Sub sample1()
Dim lrow As Integer
Dim frow As Integer
Dim row As Integer
Dim ar As Variant
Dim aar As Variant
Dim Sol(), csol()
Dim arr As Variant
Dim pos As Integer
Dim arow() As Integer
Dim rng As Range
Dim rn As Range
Dim r As Range
Dim k As Integer
Dim itr As Integer
Dim path As String
Dim tm_base As Workbook
Dim tm_data As Workbook
Dim sh_base As Worksheet
Dim sh_data As Worksheet
Dim sh As Worksheet
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Set sh = ActiveSheet
ReDim arr(0)
arr(0) = ""
path = FileSelection("Input Base")
If path = "" Then Exit Sub
Set tm_base = Workbooks.Open(path)
path = FileSelection("Input Data")
If path = "" Then Exit Sub
Set tm_data = Workbooks.Open(path)
Set sh_base = tm_base.ActiveSheet
Set sh_data = tm_data.ActiveSheet
lrow = sh_data.Cells(Rows.Count, "A").End(xlUp).row
frow = sh_base.Cells(Rows.Count, "A").End(xlUp).row
SortMacro ActiveSheet, sh_base.Range("A2:A" & frow), sh_base.Range("A1:G" & frow), 2
SortMacro ActiveSheet, sh_data.Range("C2:C" & lrow), sh_data.Range("A1:G" & lrow), 2
For row = 2 To frow
If sh_base.Cells(row, "H") <> "Done" Then
itr = 1
sh_data.Range("A1:G" & lrow).AutoFilter Field:=4, Criteria1:="="
op2:
sh_data.Range("A1:G" & lrow).AutoFilter Field:=1, Criteria1:=sh_base.Cells(row, "C").Value
sh_data.Range("A1:G" & lrow).AutoFilter Field:=2, Criteria1:=sh_base.Cells(row, "D").Value
Set rn = Nothing
On Error Resume Next
Set rn = sh_data.Range("C2:C" & lrow).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not rn Is Nothing Then
ReDim ar(0)
ReDim arow(0)
k = 1
For Each r In rn
ReDim Preserve arow(k)
ReDim Preserve ar(k)
ar(k) = r.Value
arow(k) = r.row
k = k + 1
Next
ReDim Sol(LBound(ar) To UBound(ar))
ReDim csol(LBound(ar) To UBound(ar))
limsum = sh_base.Cells(row, "A").Value
For i = LBound(ar) To UBound(ar)
If ar(i) > limsum Then
ar(i) = -1
End If
Next
maxsum = 0
findsum ar, Sol, csol, maxsum, limsum, UBound(ar), UBound(ar)
ss = ""
For i = 1 To Sol(0)
'ss = ss & sep & ar(sol(i))
'sep = ","
If Not arr(UBound(arr)) = "" Then
ReDim Preserve arr(UBound(arr) + 1)
End If
arr(UBound(arr)) = ar(Sol(i))
Next i
'MsgBox ss & " sum =" & maxsum
For j = LBound(arr) To UBound(arr)
pos = Application.Match(arr(j), ar, False)
If ar(pos - 1) > 0 Then
ar(pos - 1) = -1
End If
pos = arow(pos - 1)
If sh.Range("B1") = "Option 01" Then
sh_data.Cells(pos, "D") = sh_base.Cells(row, "B").Value
Else
sh_data.Cells(pos, "D") = sh_base.Cells(row, "B").Value & " " & Format(itr, "00")
End If
Next
ReDim arr(0)
arr(0) = ""
sh_base.Cells(row, "H") = "Done"
If sh.Range("B1") = "Option 02" Then
sh_data.Range("A1:G" & lrow).AutoFilter Field:=4, Criteria1:="="
Set rng = sh_data.Range("A1:A" & lrow).SpecialCells(xlCellTypeVisible)
If rng.Cells.Count > 1 Then
itr = itr + 1
GoTo op2
End If
End If
End If
sh_data.Range("A1:G" & frow).AutoFilter Field:=1
sh_data.Range("A1:G" & frow).AutoFilter Field:=2
sh_data.Range("A1:G" & lrow).AutoFilter Field:=4
End If
Next
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
Sub findsum(ByVal a, ByRef Sol, ByRef csol, ByRef maxsum, ByRef limsum, si, maxcount, Optional s = 0, Optional lvl = 1, Optional dif = 100000, Optional minuscount = 0, Optional tsol, Optional j = 0)
' recursive sub
For i = LBound(a) To si
If a(i) > 0 Then
If s + a(i) > limsum Then findsum a, Sol, csol, maxsum, limsum, i - 1, maxcount, s, lvl + 1, dif, minuscount, tsol
s = s + a(i)
csol(lvl) = i
If s <= limsum Then
If s > maxsum Then ' we found a sum greater than current max we save it
maxsum = s
Sol(0) = lvl
For j = 1 To lvl
Sol(j) = csol(j)
Next j
End If
If i > LBound(a) Then ' pick another number
findsum a, Sol, csol, maxsum, limsum, i - 1, maxcount, s, lvl + 1, dif, minuscount, tsol
End If
End If
s = s - a(i)
If maxsum = limsum Then Exit For 'exit if exact match
End If
Next i
End Sub
Sub SortMacro(ws As Worksheet, rn As Range, rng As Range, ord As Integer)
ws.Sort.SortFields.Clear
ws.Sort.SortFields.Add Key:=rn, _
SortOn:=xlSortOnValues, Order:=ord, DataOption:=xlSortNormal
With ws.Sort
.SetRange rng
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
Function FileSelection(file As String)
Dim path As String
Dim st As String
Dim i As Integer
Dim j As Integer
FileSelection = ""
With Application.FileDialog(3)
.title = "Select the " & file & " file"
.AllowMultiSelect = False
.InitialFileName = st
.Show
If .SelectedItems.Count = 0 Then
MsgBox "You didn't select the file!", vbExclamation, "Canceled"
Exit Function
Else
FileSelection = .SelectedItems(1)
End If
End With
End Function
You can't. I ran it. With 20 base data points and 100 data points you already have sub findsum called 79 million times. It's a combinatorial explosion and no amount of code tweaking will fix that. You'll have to find a better algorithm.
I'm struggeling to figure out the best way to attack this problem. I'm looking to group worksheet tabs and color code them based on the suffix.
Eg:
Worksheet Names:
ToDo_XY
Done_ZY
ToDo_ZY
Done_XY
Should be:
ToDo_XY
Done_XY
ToDo_ZY
Done_ZY
I know that the worksheet name will end in "non alphanumeric character" in the 3rd last position then two letters and I need to group by the two letters.
I'm not sure if I should be using a collection, or a dictionary or somehow arrays.
Here is what I have so far:
Public Sub GroupLabSheets()
Call GetLabListFromTextFile
Dim ThirdLastCharStr As String, ThirdLastCharStrIsAlphaNumBool As Boolean, PossibleLabStr As String, PossibleLabStrExistsBool As Boolean
For Each ws In ActiveWorkbook.Sheets
ThirdLastCharStr = Mid(ws.Name, Len(ws.Name) - 3, 1)
ThirdLastCharStrIsAlphaNumBool = IsAlphaNumeric(ThirdLastCharStr)
PossibleLabStr = Right(ws.Name, 2)
PossibleLabStrExistsBool = mylabs.Exists(PossibleLabStr)
If ThirdLastCharStrIsAlphaNumBool = False And PossibleLabStrExistsBool = True Then
Debug.Print "Worksheet Name = " & ws.Name & " - Index = " & ws.Index
End If
Next ws
Dim WSArr As Variant
WSArr = Array("ToDo_XY", "Done_XY")
'WSArr.Move Before:=Sheets(1)
Dim i As Long
For i = LBound(WSArr) To UBound(WSArr)
Debug.Print Worksheets(WSArr(i)).Name
Worksheets(WSArr(i)).Tab.Color = WHLabTabColor
Worksheets(WSArr(i)).Move Before:=Sheets(1)
Next i
End Sub
Public Function IsAlphaNumeric(ByVal vInput As Variant) As Boolean
On Error GoTo Error_Handler
Dim oRegEx As Object
If IsNull(vInput) = False Then
Set oRegEx = CreateObject("VBScript.RegExp")
oRegEx.Pattern = "^[a-zA-Z0-9]+$"
IsAlphaNumeric = oRegEx.Test(vInput)
Else
IsAlphaNumeric= True 'Null value returns True, modify as best suits your needs
End If
Error_Handler_Exit:
On Error Resume Next
If Not oRegEx Is Nothing Then Set oRegEx = Nothing
Exit Function
Error_Handler:
Debug.Print "The following error has occured" & vbCrLf & vbCrLf & _
"Error Number: " & err.Number & vbCrLf & _
"Error Source: IsAlphaNumeric" & vbCrLf & _
"Error Description: " & err.Description & _
Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
, vbOKOnly + vbCritical, "An Error has Occured!"
Resume Error_Handler_Exit
End Function
Try this:
Sub ArrangeSheets()
Dim i As Long, wb As Workbook, ws As Worksheet
Dim dict As Object, suffix, colors, col As Collection, n As Long
colors = Array(vbRed, vbYellow, vbGreen, vbBlue) 'colors to be applied (may need to add more...)
Set dict = CreateObject("scripting.dictionary")
Set wb = ThisWorkbook
'collect and group all matched worksheets according to their suffix
For Each ws In wb.Worksheets
If SortIt(ws) Then
suffix = Right(ws.Name, 2)
If Not dict.exists(suffix) Then dict.Add suffix, New Collection
dict(suffix).Add ws
End If
Next ws
'now loop over the groups and move all sheets in a group
' after the first sheet in that group
For i = 0 To dict.Count - 1
Set col = dict.Items()(i)
For n = 1 To col.Count
Set ws = col(n)
ws.Tab.Color = colors(i)
If n > 1 Then ws.Move after:=col(n - 1)
Next n
Next i
End Sub
'is this worksheet a candidate for sorting?
Function SortIt(ws As Worksheet) As Boolean
Dim nm As String
nm = UCase(ws.Name)
If Len(nm) >= 4 Then
SortIt = (Not Mid(nm, Len(nm) - 2, 1) Like "[A-Z0-9]") And _
Right(nm, 2) Like "[A-Z][A-Z]"
End If
End Function
Try this code:
Option Explicit
Sub RearrangeTabs()
Dim a() As String, i As Integer, j As Integer, buf As String, ws As Worksheet
Dim colour As Long
With ActiveWorkbook
ReDim a(1 To .Worksheets.Count, 1 To 2)
i = 1
For Each ws In .Worksheets
buf = ws.Name
' make sort key
a(i, 1) = Right(buf, 2) & IIf(Left(buf, 1) = "T", "A", "Z")
a(i, 2) = buf
i = i + 1
Next
' primitive bubble sort
For i = LBound(a, 1) To UBound(a, 1)
For j = LBound(a, 1) To UBound(a, 1)
If a(i, 1) < a(j, 1) Then
buf = a(i, 1): a(i, 1) = a(j, 1): a(j, 1) = buf
buf = a(i, 2): a(i, 2) = a(j, 2): a(j, 2) = buf
End If
Next j
Next i
colour = 3 'start ColorIndex (built-in set of colors [1..56])
For i = UBound(a, 1) To LBound(a, 1) Step -1
Set ws = .Worksheets(a(i, 2))
ws.Tab.ColorIndex = colour
ws.Move Before:=.Worksheets(1)
' increment ColorIndex for every odd i
If i Mod 2 = 1 Then colour = colour Mod 56 + 1
Next i
End With
End Sub
Before
After
I have a script that maps out data and prints an array to a template based on when values in a column change (when the next set of duplicates start), basically stops and prints and array when cells M2<>M3 type deal.
It goes through and saves off a copy from the template for everyone but the final set of data, it just sits in the template and doesn't save off. How can I edit my code to go through all values and not leave the last set of data for me to manually save off?
Option Explicit
Sub Main()
Dim wb As Workbook
Dim Report_Data, Last, Login
Dim i As Long, j As Long, k As Long, a As Long
Dim Destination_Rng As Range
Workbooks.Open filename:="C:\Goal_Report_Template.xlsx"
Set wb = Workbooks("Goal_Report_Template.xlsx")
Set Destination_Rng = wb.Sheets("Sheet1").Range("A2")
With ThisWorkbook.Sheets("Q1 report")
Report_Data = .Range("W2", .Range("A" & Rows.Count).End(xlUp))
End With
wb.Activate
Application.ScreenUpdating = False
For i = 1 To UBound(Report_Data)
If Report_Data(i, 14) <> Last Then
If i > 1 Then
Destination_Rng.Select
wb.SaveCopyAs ThisWorkbook.Path & Application.PathSeparator & _
ValidFileName(Login & " - " & Last & " - Goal Reporting.xlsx")
End If
Rows(1).Offset(1, 0).Resize(Rows.Count - 1).EntireRow.ClearContents
Last = Report_Data(i, 14)
Login = Report_Data(i, 13)
j = 0
End If
a = 0
For k = 1 To UBound(Report_Data, 2)
Destination_Rng.Offset(j, a) = Report_Data(i, k)
a = a + 1
Next
j = j + 1
Next
End Sub
You need to perform another SaveAs after exiting the i loop. You can avoid duplicating code by breaking that out into a separate sub.
Untested:
Sub Main()
Dim wb As Workbook
Dim Report_Data, Last, Login, Current
Dim i As Long, j As Long, k As Long, a As Long
Dim Destination_Rng As Range
Set wb = Workbooks.Open(Filename:="C:\Goal_Report_Template.xlsx")
Set Destination_Rng = wb.Sheets("Sheet1").Range("A2")
With ThisWorkbook.Sheets("Q1 report")
Report_Data = .Range("W2", .Range("A" & .Rows.Count).End(xlUp))
End With
Application.ScreenUpdating = False
For i = 1 To UBound(Report_Data)
Current = Report_Data(i, 14)
If Current <> Last Then
If i > 1 Then SaveCopy wb, Login, Last '<< save this one
Destination_Rng.CurrentRegion.Offset(1, 0).ClearContents
Login = Report_Data(i, 13)
j = 0
Else
j = j + 1
End If
For k = 1 To UBound(Report_Data, 2)
Destination_Rng.Offset(j, k - 1) = Report_Data(i, k)
Next k
Next i
SaveCopy wb, Login, Last '<< save the last report
End Sub
Sub SaveCopy(wb As Workbook, Login, Last)
wb.SaveCopyAs ThisWorkbook.Path & Application.PathSeparator & _
ValidFileName(Login & " - " & Last & " - Goal Reporting.xlsx")
End Sub
Hopefully the title is clear. I am trying to search through multiple tables on a single sheet. The information I am looking for is the same for all of the tables, just that the corresponding column is located in different spots (e.g. in one table the column I want to search is in I, while for another table it could be in O.) which makes it a bit more challenging for me.
I want to search through each column that has the same title (Load Number) and depending on its value, copy that entire row over to a sheet that corresponds with that value.
Below is what I have so far in VBA as well as a picture to hopefully clarify my issue.
Any help/advice is appreciated!
http://imgur.com/a/e9DyH
Sub Load_Number_Finder()
Dim ws As Worksheet
Dim i As Integer
Dim j As Integer
j = 1
Set ws = Sheets.Add(After:=Sheets("Master"))
ws.Name = ("Test Load " & j)
i = 1
Sheets("Master").Select
For Each cell In Sheets("Master").Range("M:M")
If cell.Value = "1" Then
j = 1
'Set WS = Sheets.Add(After:=Sheets("Master"))
'WS.Name = ("Test Load " & j)
matchRow = cell.Row
Rows(matchRow & ":" & matchRow).Select
Selection.Copy
Sheets("Test Load " & j).Select
ActiveSheet.Rows(i).Select
ActiveSheet.Paste
Sheets("Master").Select
i = i + 1
ElseIf cell.Value = "" Then
' 2, 3, 4, 5, cases
Else
' Something needs to go here to catch when it doesnt have a load number on it yet
End If
' Err_Execute:
' MsgBox "An error occurred."
Next
End Sub
Try this function. This should work for you. Let me know what the results are with your sheet. I made a mock up sheet and tested it, it worked. I can make changes if this is not exactly what you are looking for.
Option Explicit
Sub copyPaste()
Dim rowCount, row_ix, temp, i As Integer
Dim TD_COL_IX As Integer
Dim td_value As String
Dim td_values() As String
rowCount = Worksheets("Master").Cells(Rows.Count, "A").End(xlUp).Row
For row_ix = 1 To rowCount
temp = isNewTable(CInt(row_ix))
If temp > 0 Then
TD_COL_IX = temp
ElseIf TD_COL_IX > 0 Then
td_value = Worksheets("Master").Cells(row_ix, TD_COL_IX)
If Not td_value = "" Then
td_values = Split(td_value, " ")
For i = 0 To UBound(td_values)
If Not sheetExists("Test Load " & td_values(i)) Then
Sheets.Add.Name = "Test Load " & td_values(i)
End If
If Worksheets("Test Load " & td_values(i)).Cells(1, 1).Value = "" Then
Worksheets("Master").Range(Worksheets("Master").Cells(row_ix, 1), Worksheets("Master").Cells(row_ix, TD_COL_IX - 1)).Copy _
Destination:=Worksheets("Test Load " & td_values(i)).Cells(1, 1)
Else
Dim rowCount_pasteSheet As Integer
rowCount_pasteSheet = Worksheets("Test Load " & td_values(i)).Cells(Rows.Count, "A").End(xlUp).Row
Worksheets("Master").Range(Worksheets("Master").Cells(row_ix, 1), Worksheets("Master").Cells(row_ix, TD_COL_IX - 1)).Copy _
Destination:=Worksheets("Test Load " & td_values(i)).Cells(rowCount_pasteSheet + 1, 1)
End If
Next i
End If
End If
Next row_ix
End Sub
Function isNewTable(row_ix As Integer) As Integer
Dim colCount, col_ix As Integer
colCount = Worksheets("Master").Cells(row_ix, Columns.Count).End(xlToLeft).Column
For col_ix = 1 To colCount
If Not IsError(Worksheets("Master").Cells(row_ix, col_ix).Value) Then
If Worksheets("Master").Cells(row_ix, col_ix).Value = "LD #" Then
isNewTable = col_ix
Exit Function
End If
End If
Next col_ix
isNewTable = 0
End Function
' ####################################################
' sheetExists(sheetToFind As String) As Boolean
'
' Returns true if the sheet exists, False otherwise
' ####################################################
Public Function sheetExists(sheetToFind As String) As Boolean
Dim sheet As Worksheet
sheetExists = False
For Each sheet In Worksheets
If sheetToFind = sheet.Name Then
sheetExists = True
Exit Function
End If
Next sheet
End Function
I would like to consolidate rows with checked checkboxes from three sheets (“Liver”, ”Lung” and “Kidney”) into one sheet "Report". I would like to grab rows that do not contain word "sample" in column A. When I paste the data into "Report" I would like to label each group of rows with the corresponding originating sheet name by adding a row in between that contains the sheet name, in column A.
I came up with this code which goes into an infinite loop and I have to kill Excel to stop it. This is just for "Lung" sheet only but I'm hoping to reproduce it for the other two sheets.
Ideally, I would like to use arrays to transfer the data but I'm not sure how to work it out. Any suggestions on how to fix what I already have or to improve it would be greatly appreciated.
Thank you
For Each chkbx In ActiveSheet.CheckBoxes
If chkbx.Value = 1 Then
For r = 2 To Rows.count
If Cells(r, 1).Top = chkbx.Top And InStr(Cells(r, 1).Value, "Sample") < 0 Then
'
With Worksheets("Report")
LRow = .Range("A" & Rows.count).End(xlUp).Row + 1
.Range("A" & LRow & ":P" & LRow) = _
Worksheets("Lung").Range("A" & r & ":P" & r).Value
End With
Exit For
End If
Next r
End If
Next
The code bellow will generate the following reports (details bellow):
.
There are 3 sections, but all code should be pasted into one user module:
.
Subs to execute:
Option Explicit
Private Const REPORT As String = "Report_"
Private Const EXCLUDE As String = "Sample"
Private Const L_COL As String = "P"
Private wsRep As Worksheet
Private lRowR As Long
Public Sub updateSet1()
updateSet 1
End Sub
Public Sub updateSet2()
updateSet 2
End Sub
Public Sub updateSet3()
updateSet 3
End Sub
Public Sub updateSet(ByVal id As Byte)
Application.ScreenUpdating = False
showSet id
Application.ScreenUpdating = True
End Sub
Public Sub consolidateAllSheets()
Application.ScreenUpdating = False
With ThisWorkbook
consolidateReport .Worksheets("COLON"), True 'time stamp to 1st line of report
consolidateReport .Worksheets("LUNG")
consolidateReport .Worksheets("MELANOMA")
wsRep.Rows(lRowR).Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
Application.ScreenUpdating = True
End Sub
.
showSet() - use 1 for Set1, 2 for Set2, 3 for Set2 edited:
Public Sub showSet(ByVal id As Byte)
Dim ws As Worksheet, cb As Shape, lft As Double, mid As Double, thisWs As Worksheet
Dim lRed As Long, lBlu As Long, cn As String, cbo As Object, s1 As Boolean
If id <> 1 And id <> 2 And id <> 3 Then Exit Sub
lRed = RGB(255, 155, 155): lBlu = RGB(155, 155, 255)
Set thisWs = ThisWorkbook.ActiveSheet
For Each ws In ThisWorkbook.Worksheets
If InStr(1, ws.Name, REPORT, vbTextCompare) = 0 Then
lft = ws.Cells(1, 2).Left
mid = lft + ((ws.Cells(1, 2).Width / 2) - 5)
For Each cb In ws.Shapes
cn = cb.Name
Set cbo = cb.OLEFormat.Object
s1 = InStr(1, cn, "set1", 1) > 0
If id < 3 Then
cb.Visible = IIf(s1, (id = 1), (id <> 1))
cb.Left = IIf(cb.Visible, mid, lft)
cbo.Interior.Color = IIf(s1, lBlu, lRed)
Else
cb.Visible = True
cb.Left = IIf(s1, lft + 3, mid + 6.5)
cbo.Interior.Color = IIf(s1, lBlu, lRed)
End If: ws.Activate
With cbo
.Width = 15
.Height = 15
End With
Next
Else
ws.Visible = IIf((id = 3), -1, IIf(InStr(1, ws.Name, id) = 0, 0, -1))
End If
Next
thisWs.Activate 'to properly update checkbox visibility
End Sub
.
consolidateReport()
Public Sub consolidateReport(ByRef ws As Worksheet, Optional dt As Boolean = False)
Dim fRowR As Long, vSetID As Byte, vSetName As String
Dim lRow As Long, thisRow As Long, cb As Variant
vSetID = IIf(ws.Shapes("cbSet2_03").Visible, 2, 1)
vSetName = "Set" & vSetID
Set wsRep = ThisWorkbook.Worksheets(REPORT & vSetID)
fRowR = wsRep.Range("A" & wsRep.Rows.count).End(xlUp).Row
If Not ws Is Nothing Then
With ws
lRow = .Range("A" & .Rows.count).End(xlUp).Row
lRowR = fRowR + 1
With wsRep.Cells(lRowR, 1)
.Value2 = ws.name
.Interior.Color = vbYellow
If dt Then .Offset(0, 2) = Format(Now, "mmm dd yyyy, hh:mm AMPM")
End With
For Each cb In .Shapes
If InStr(1, cb.name, vSetName, 0) Then
If cb.OLEFormat.Object.Value = 1 Then
thisRow = cb.TopLeftCell.Row
If InStr(1, .Cells(thisRow, 1).Value2, EXCLUDE, 1) = 0 Then
lRowR = lRowR + 1
wsRep.Range("A" & lRowR & ":" & L_COL & lRowR).Value2 = _
.Range("A" & thisRow & ":" & L_COL & thisRow).Value2
End If
End If
End If
Next
If fRowR = lRowR - 1 Then
wsRep.Cells(lRowR, 1).EntireRow.Delete
lRowR = lRowR - 1
MsgBox "No checkboxes checked for sheet " & ws.name
End If
End With
End If
End Sub
.
The process starts with one file, expected to have 2 sets of checkboxes on each sheet (column 2):
cbSet1_01, cbSet1_02, cbSet1_03...
cbSet2_01, cbSet2_02, cbSet2_03...
as in this image
(check-box colors will be reset by code as long as they follow the naming convention above)
.
Generate two files, one for Set1, the other for Set2 by running Sub updateSet()
showSet 1 hides Set2 (Report_2 and all checkboxes, on all sheets) - Save File1
showSet 2 hides Set1 (Report_1 and all checkboxes, on all sheets) - Save File2
Distribute, then retrieve the updated files
Open File1 and run Sub consolidateAllSheets() to generate Report_1
Open File2 and run Sub consolidateAllSheets() to generate Report_2
Compare Report_1 to Report_2
Generate Set 2 for editing by running Sub updateSet()
showSet 3 shows Set1 and Set2 (all checkboxes, and both reports) - Save File3
Compare File1, File2, and File3