Inserting checkbox values from VBA form into Excel Spreadsheet - excel

I am trying to insert a list of selected checkboxes into a spreadsheet, within this use case, a user can choose up to 15 items. This will be inserted into a certain cell which I have defined below.
I have a checkbox with the following names/values:
Name Value
========== =====
chk_week1 1
chk_week2 2
... ...
... ...
chk_week15 15
For example if the user selects chk_week1, chk_week2, chk_week4 and chk_week5, then it should be inserted into the cell as 1,2,4,5.
I've included an image how it looks like to better demonstrate it:
Each checkbox has the name and value listed in the table above. Here is the code I am using so far:
Private Sub btnSubmit_Click()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Worksheets("main")
' Copy the data to the database
' Get last empty cell in column A
Set rng1 = ws.Cells(Rows.Count, "a").End(xlUp)
' Having difficulty adding the code here
' rng1.Offset(1, 7) = weeks
End Sub
Thanks in advance.

This function would return the string you're wanting to put in the cell.
Function CheckBoxValues() As String
For x = 1 To 15
If Sheets("Main").Shapes("chk_week" & x).OLEFormat.Object.Object.Value Then
CheckBoxValues = CheckBoxValues & x & ","
End If
Next
if Len(CheckBoxValue <> 0) then
CheckBoxValues = Left(CheckBoxValues, Len(CheckBoxValues) - 1)
end if
End Function
Or for the non-looping method, check Francis Dean's solution.

You can use a function to go through your check boxes and return a string in your desired format as such (add on the rest of the check boxes!)
Private Sub btnSubmit_Click()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Worksheets("main")
' Copy the data to the database
' Get last empty cell in column A
Set rng1 = ws.Cells(Rows.Count, "a").End(xlUp)
' Having difficulty adding the code here
rng1.Offset(1, 7) = GetWeeks
End Sub
Private Function GetWeeks() As String
Dim weeks As String
'Add values to the string if condition is true
If chk_week1.Value = True Then weeks = weeks & "1,"
If chk_week2.Value = True Then weeks = weeks & "2,"
If chk_week3.Value = True Then weeks = weeks & "2,"
'...
If chk_week14.Value = True Then weeks = weeks & "14,"
If chk_week15.Value = True Then weeks = weeks & "15,"
'Remove the trailing comma
If Right(weeks, 1) = "," Then weeks = Left(weeks, Len(weeks) - 1)
GetWeeks = weeks
End Function

Related

How can I hide the unused columns and rows of an Excel worksheet near cells with comments?

I have some VBA code that queries and creates a table of data in a worksheet with comments in some of the cells.
It's occupies a range of about A1:N200 and then I want to hide the unused space like in the code below.
Worksheet.Columns(Number_of_columns + 1).Resize(, Worksheet.Columns.Count - Number_of_columns).EntireColumn.Hidden = True
Worksheet.Rows(Number_of_rows & ":" & Worksheet.Rows.Count).EntireRow.Hidden = True
Doing so throws the run-time error 1004:
Unable to set the hidden property of the range class
I have checked in a new Excel file, added a comment to the cell A1 and then tried hidding the other columns and rows but had to leave 4 columns (A to D, but could have been for example A and C to E) and 5 rows visible. If I try hidding more I get the message Cannot shift objects off sheet.
Below is an example of a procedure that throws errors when ran in a new Excel file.
Private Sub Procedure()
Dim Worksheet As Excel.Worksheet
Dim Range As Excel.Range
Set Worksheet = Excel.Application.ThisWorkbook.ActiveSheet
Set Range = Worksheet.Cells(1, 1)
If Range.Comment Is Nothing Then
Range.AddComment
End If
Set Range = Worksheet.Columns(2).Resize(, Worksheet.Columns.Count - 1).EntireColumn
Range.Select ' Just to test the range, it works. Columns B to XFD
Range.Hidden = True ' Throws error
Set Range = Worksheet.Rows(2 & ":" & Worksheet.Rows.Count).EntireRow
Range.Select ' Just to test the range, it works. Rows 2 to 1048576
Range.Hidden = True ' Throws error
End Sub
Is there any way to hide them so that only the data is visible? The only workaround (not solution) that I can think of is removing the comments, hidding the columns and rows, and then adding the comments back, which is undesirable.
I am not sure if I understand correctly, would be easier to see Excel, below is the sample code, just tested on Excel 2016:
Sub StackOverflow()
Dim lngLastRow As Long
Dim lngLastColumn As Long
Dim lngLastColLetter As String
Dim shtWorking As Object
Set shtWorking = ActiveSheet
'find last row
lngLastRow = shtWorking.Cells(shtWorking.Rows.Count, 1).End(-4162).Row + 1
'find last column
lngLastColumn = shtWorking.Cells(1, shtWorking.Columns.Count).End(-4159).Column + 1
'convert last column index to last column letter
lngLastColLetter = Split(shtWorking.Cells(1, lngLastColumn).Address, "$")(1)
'hide columns
shtWorking.Columns(lngLastColLetter & ":XFD").EntireColumn.Hidden = True
'hide rows
shtWorking.Rows(lngLastRow & ":" & shtWorking.Rows.Count).EntireRow.Hidden = True
set shtWorking=nothing
End Sub

How do you add a variable.value as the parameter of range()?

I'm trying to merge the first three empty rows and write Activity# in the three merged cells. I can't even figure out how to select 3 custom cells to get them ready for merging. I checked everywhere online but range(A1:B2) is always given a definitive range. How do I write for example: range(variable_A1:variable_B2)?
This is my code so far:
Private Sub OKButton_Click()
'Make Sheet1 active
Sheet1.Activate
Dim beginning
Dim ending
Dim selection
beginning = Cells(empty_row.Value, 2)
ending = Cells(empty_row.Value + 2, 2)
'this is supposed to select 3 cells, but it doesn't work
selection = Range("beginning:ending").Select
'figure out how to merge cells below
Cells(empty_row.Value, 2).Value = "Activity" & Activity_number.Value
Dim i As Integer
For i = 1 To nb_subs.Value
Cells(empty_row.Value + i + 2, 2).Value = "Sub-Activity" & i
Next i
Private Sub OKButton_Click()
Dim beginning As Range
Dim ending As Range
Dim selection As Range
With Sheet1
Set beginning = .Cells(empty_row.Value, 2)
Set ending = .Cells(empty_row.Value + 2, 2)
'this is supposed to select 3 cells, but it doesn't work
Set selection = .Range(beginning, ending)
selection.Merge
selection.Value = "Activity" & Activity_number.Value
Dim i As Integer
For i = 1 To nb_subs.Value
.Cells(empty_row.Value + i + 2, 2).Value = "Sub-Activity" & i
Next i
End With

VBA - Prevent the Adding of Multiple Sheets

The purpose of my macro is to allow a user to select a range in their model that they want to check for hard codes. The macro then prints the worksheet, cell address, and value of the hard code on a summary sheet. The macro currently works great if you're selecting only from one sheet; however, if you extend your selection to multiple sheets, the macro will create multiple sheets instead of just one which it is intended to do. Thank you in advance for your time and help
Set RngCon = Selection.SpecialCells(xlCellTypeConstants, xlNumbers)
Set SumWS = Worksheets.Add
Username = InputBox("Please create a name for the output sheet (i.e. Whs Industry Hard Codes)")
SumWS.Name = Username
x = 1
SumWS.Cells(x, 1) = "Worksheet"
SumWS.Cells(x, 2) = "Address"
SumWS.Cells(x, 3) = "Value"
For Each c In RngCon
x = x + 1
SumWS.Cells(x, 1) = c.Worksheet.Name
SumWS.Cells(x, 2) = c.Address(False, False)
SumWS.Cells(x, 3) = c.Value
Next c
you could do something like that:
Sub test()
Dim SumWS As Worksheet
Dim ws As Worksheet
Dim SelectedSheets() As String
Dim n As Long
Dim i As Long
n = 0
For Each ws In ActiveWindow.SelectedSheets
ReDim Preserve SelectedSheets(n)
SelectedSheets(n) = ws.Name
n = n + 1
Next
Sheets(SelectedSheets(0)).Select
Set SumWS = Worksheets.Add
Debug.Print "Sum Sheet: " & SumWS.Name
For i = LBound(SelectedSheets) To UBound(SelectedSheets)
Debug.Print "Selected Sheet #" & i & ": " & SelectedSheets(i)
Next i
End Sub
In the first for you save the selected sheets in an array. Then you can select one specific sheet and add your sum sheet. The second for shows how to work with the stored information. You can loop the selected sheets to get all values and - if needed - select them again.
credits to Siddharth Rout (Similar case)

Most efficient way to delete row with VBA

I currently have a macro that I use to delete a record if the ID doesn't exist in a list of ID's I created from an XML document. It does work like I want it to, however I have over 1000 columns in the spreadsheet (one for each day of the year until end of 2015) so it takes ages to delete the row and it can only do 1 or 2 before it says "Excel ran out of resources and had to stop". Below is the code I'm using for the macro, is there another way I can do this so that Excel doesn't run of of resources?
Sub deleteTasks()
Application.ScreenUpdating = False
Dim search As String
Dim sheet As Worksheet
Dim cell As Range, col As Range
Set sheet = Worksheets("misc")
Set col = sheet.Columns(4)
ActiveWorkbook.Sheets("Schedule").Activate
ActiveSheet.Range("A4").Select
ActiveSheet.Unprotect
ActiveSheet.Range("A:C").EntireColumn.Hidden = False
Do While ActiveCell.Value <> ""
search = ActiveCell.Value
Set cell = col.Find(What:=search, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If cell Is Nothing Then 'If the taskID is not in the XML list
Debug.Print "Deleted Task: " & ActiveCell.Value
Selection.EntireRow.Delete
End If
ActiveCell.Offset(1, 0).Select 'Select next task ID
Loop
ActiveSheet.Range("A:B").EntireColumn.Hidden = True
ActiveSheet.Protect
End Sub
After trying lots of different options, including all the answers listed below. I have realized that whatever the method is, deleting a row with ~1100 columns is going to take a while on my average laptop (2.20 Ghz, 4GB RAM). Since the majority of the rows are empty I have found alternative method which is a lot faster. I just clear the cells which contain data (A:S) and then resize the table to remove the row where I just deleted the data from. This end result is exactly the same as entireColumn.Delete. Below is the code I'm using now
'New method - takes about 10 seconds on my laptop
Set ws = Worksheets("Schedule")
Set table = ws.ListObjects(1)
Set r = ws.Range("A280:S280")
r.Clear
table.Resize Range("A3:VZ279")
Using anything involving EntireColumn.Delete or just manually selecting the row and deleting it takes about 20-30 seconds on my laptop. Of course this method only works if your data is in a table.
The short answer:
Use something like
ActiveSheet.Range(DelStr).Delete
' where DelStr = "15:15" if you want to delete row 15
' = "15:15,20:20,32:32" if you want to delete rows 15,20 and 32
The long answer:
Important: If you have ~ 30 / 35 rows to delete, the following code works very efficiently. Beyond which it would throw up an error. For code to handle arbitrary number of rows efficiently see the very long answer below this.
If you have a function which lets you list out which rows you want to delete, try the code below. This is what I use to very efficiently delete multiple rows with minimum overhead. (the example assumes that you've obtained the rows you need to delete through some program, here I manually feed them in):
Sub DeleteRows()
Dim DelRows() As Variant
ReDim DelRows(1 To 3)
DelRows(1) = 15
DelRows(2) = 18
DelRows(3) = 21
'--- How to delete them all together?
Dim i As Long
For i = LBound(DelRows) To UBound(DelRows)
DelRows(i) = DelRows(i) & ":" & DelRows(i)
Next i
Dim DelStr As String
DelStr = Join(DelRows, ",")
' DelStr = "15:15,18:18,21:21"
'
' IMPORTANT: Range strings have a 255 character limit
' See the other code to handle very long strings
ActiveSheet.Range(DelStr).Delete
End Sub
The (very long) efficient solution for arbitrary number of rows and benchmark results:
Here are the benchmark results obtained by deleting rows (Time in seconds vs. no. of rows).
The rows are on a clean sheet and contain a volatile formula in the D column from D1:D100000
i.e. for 100,000 rows, they have a formula =SIN(RAND())
The code is long and not too pretty, but it splits the DelStr into 250 character substrings and forms a range using these. Then the new DeleteRng range is deleted in a single operation.
The time to delete may depend on the contents of the cells. The testing/benchmarking, in congruence with a bit of intuition suggests the following results.
Sparse rows/empty cells delete fastest
Cells with values take somewhat longer
Cells with formulas take even longer
Cells which feed into formulas in other cells take longest as their deletion triggers the #Ref reference error.
Code:
Sub DeleteRows()
' Usual optimization
' Events not disabled as sometimes you'll need to interrupt
' You can optionally keep them disabled
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
' Declarations...
Dim DelRows() As Variant
Dim DelStr As String, LenStr As Long
Dim CutHere_Str As String
Dim i As Long
Dim MaxRowsTest As Long
MaxRowsTest = 1000
' Here I'm taking all even rows from 1 to MaxRowsTest
' as rows to be deleted
ReDim DelRows(1 To MaxRowsTest)
For i = 1 To MaxRowsTest
DelRows(i) = i * 2
Next i
'--- How to delete them all together?
LenStr = 0
DelStr = ""
For i = LBound(DelRows) To UBound(DelRows)
LenStr = LenStr + Len(DelRows(i)) * 2 + 2
' One for a comma, one for the colon and the rest for the row number
' The goal is to create a string like
' DelStr = "15:15,18:18,21:21"
If LenStr > 200 Then
LenStr = 0
CutHere_Str = "!" ' Demarcator for long strings
Else
CutHere_Str = ""
End If
DelRows(i) = DelRows(i) & ":" & DelRows(i) & CutHere_Str
Next i
DelStr = Join(DelRows, ",")
Dim DelStr_Cut() As String
DelStr_Cut = Split(DelStr, "!,")
' Each DelStr_Cut(#) string has a usable string
Dim DeleteRng As Range
Set DeleteRng = ActiveSheet.Range(DelStr_Cut(0))
For i = LBound(DelStr_Cut) + 1 To UBound(DelStr_Cut)
Set DeleteRng = Union(DeleteRng, ActiveSheet.Range(DelStr_Cut(i)))
Next i
DeleteRng.Delete
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
The code to generate the formulas in a blank sheet is
Sub FillRandom()
ActiveSheet.Range("D1").FormulaR1C1 = "=SIN(RAND())"
Range("D1").AutoFill Destination:=Range("D1:D100000"), Type:=xlFillDefault
End Sub
And the code to generate the benchmark results above is
Sub TestTimeForDeletion()
Call FillRandom
Dim Time1 As Single, Time2 As Single
Time1 = Timer
Call DeleteRows
Time2 = Timer
MsgBox (Time2 - Time1)
End Sub
Note: Many thanks to brettdj for pointing out the error which gets thrown when the length of DelStr exceeding 255 characters. It seems to be a known problem and as I painfully found out, it still exists for Excel 2013.
This code uses AutoFilter and is significantly faster than looping through rows.I use it daily and it should be pretty easy to figure out.Just pass it what you're looking for and the column to search in.You could also hard-code the column if you want.
private sub PurgeRandy
Call FindDelete("F", "Randy")
end sub
Public Sub FindDelete(sCOL As String, vSearch As Variant) 'Simple find and Delete
Dim lLastRow As Integer
Dim rng As Range
Dim rngDelete As Range
Range(sCOL & 1).Select
[2:2].Insert
[2:2] = "***"
Range(sCOL & ":" & sCOL).Select
With ActiveSheet
.UsedRange
lLastRow = .Cells.SpecialCells(xlCellTypeLastCell).Row
Set rng = Range(sCOL & 2, Cells(lLastRow, sCOL))
rng.AutoFilter Field:=1, Criteria1:=vSearch
Set rngDelete = rng.SpecialCells(xlCellTypeVisible)
rng.AutoFilter
rngDelete.EntireRow.Delete
.UsedRange
End With
End Sub
In this case a simple working formula can be used to see if each of the values in your range to be tested (column A of schedule) exist in column F of misc
In B4 it would =MATCH(A4,misc!D:D,0)
This can be used manually or with code for an efficient delete as the formula by design returns an error if there is no match which we can efficiently delete with VBA with either:
AutoFilter
SpecialCells (the design piece*)
In xl2007 note that there is a limit of 8192 discrete areas that can be selected with SpecialCells
code
Sub ReCut()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim rng1 As Range
Set ws1 = Sheets("misc")
Set ws2 = Sheets("schedule")
With Application
.ScreenUpdating = False
.EnableEvents = False
.DisplayAlerts = False
End With
Set rng1 = ws2.Range(ws2.[a4], ws2.Cells(Rows.Count, "A").End(xlUp))
ws2.Columns(2).Insert
With rng1.Offset(0, 1)
.FormulaR1C1 = "=MATCH(RC[-1],'" & ws1.Name & "'!C[2],0)"
On Error Resume Next
.Cells.SpecialCells(xlCellTypeFormulas, xlErrors).EntireRow.Delete
On Error GoTo 0
End With
ws2.Columns(2).Delete
With Application
.ScreenUpdating = True
.EnableEvents = True
.DisplayAlerts = True
End With
End Sub
Note: I don't have enough "reputation" to add my comments thus posting as answer. Credit to hnk for wonderful answer (Long Answer). I have one edit as suggestion:
Once you split the long string and in case the last block is more than the set character then it is having "!" at the end which is throwing error for range method. Addition of IF statement and MID is ensuring that there is no such character.
To handle that, use:
For i = LBound(DelStr_Cut) + 1 To UBound(DelStr_Cut)
If Right(DelStr_Cut(i), 1) = "!" Then
DelStr_Cut(i) = Mid(DelStr_Cut(i), 1, Len(DelStr_Cut(i)) - 1)
Set DeleteRng = Union(DeleteRng, ActiveSheet.Range(DelStr_Cut(i)))
Else
Set DeleteRng = Union(DeleteRng, ActiveSheet.Range(DelStr_Cut(i)))
End If
Next i
Thanks,
Bakul

Entereing multiple values in a single cell in excel

I want to enter multiple values in a single cell in excel sheet based on the certain condition as in if there are multiple sheets in the workbook then if any of the sheet starting with name TC contains color in it then I've to enter the information in Read Me Section of the Excel Workbook a another worksheet. The problem with my code is that its not displaying unique sheets which contain coloring...Suppose Sheet "TC_1" and "TC_3" contains color in any of the cell then its displaying the output as ";TC_3;TC_3;TC_3;" although the expected output over here is "TC_1;TC_3".
Here, is the code:
Sub ErrorInSheet()
Dim Row
Dim Names As String
Names = ""
For Row = 2 To tsheet.UsedRange.Rows.Count
For Chkcol = 1 To tsheet.UsedRange.Columns.Count
If tsheet.Cells(Row, Chkcol).Interior.ColorIndex = 3 Then
Names = Names & ";" & tsheet.Name
End If
Next
Next Row
Sheets("Read Me").Cells(13, 5).Value = Names
End Sub
Sub iterateSheets()
For Each sheet1t In Worksheets
If InStr(1, sheet1t.Name, "TC") Then
Set tsheet = sheet1t
Call ErrorInSheet
End If
Next
End Sub
I think this will work for you - I tested it and worked for me.
Sub FindErrors()
Dim sht As Worksheet, cl As Range, shtNames As String
shtNames = vbNullString
For Each sht In Worksheets
If Left$(sht.Name, 2) = "TC" Then
For Each cl In sht.UsedRange.Cells
If cl.Interior.ColorIndex = 3 Then
shtNames = IIf(shtNames = vbNullString, sht.Name, shtNames & ";" & sht.Name)
End If
Next cl
End If
Next sht
Worksheets("Read Me").Cells(13, 5) = shtNames
End Sub
Notes:
I've explicitly declared the variables
I am assuming all your sheets start with "TC" so I've used Left$ but you can use InStr if you like
I've used the ternary IIF statement to stop you getting a leading ;
I've put all the code in one Sub but you can split it out if you like

Resources