Click and Unclick X boxes in VBA excel formula - vba

I am creating a survey that I want to be simple for the end user. I have created a document that operates on a Likert scale, where things range from Disagree to Agree on a 6 point scale (with a no answer field). The questions I have run on rows 3 to 152 and the choices are in cells C:H on each row. Currently, I have it to where the taker can click on a cell and produce an X in the box, indicating their choice. I also have it so that they may only click one option in the row, and if they select another option, it removes the first X and places the X in the new cell that they have clicked.
Here is what I want. Right now, if they place an X in a cell, and click that same X, my code goes through and takes the X away and replaces it again with that same X. I would like it to replace the clicked X with nothing, so that it can toggle on and off with a click. I want to keep it so that if they select another cell in the same row, the X in the original cell disappears and the X pops up in the clicked cell, however. I only want them to be able to select one cell in each row for each question. Sorry for the length, but I just wanted to be clear. Here is my code currently.
Thanks for any help you can offer!
Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim rInt As Range
Dim rCell As Range
For rw = 3 To 152
Set rInt = Intersect(Target, Range("C" & rw & ":H" & rw))
If Not rInt Is Nothing Then
If Application.WorksheetFunction.CountA(Range("C" & rw & ":H" & rw)) > 0 Then
Range("C" & rw & ":H" & rw).Value = ""
End If
For Each rCell In rInt
If rCell.Value = "" Then
rCell.Value = "X"
End If
Next
End If
Set rInt = Nothing
Set rCell = Nothing
Cancel = True
Next
End Sub

Try the below code. Notice I removed your loop; there's no reason to loop through every row when we can pinpoint the row the user selected and focus on that row. I also changed the way rInt is set, replaced the CountA function with .Find, and wrapped the entire procedure with a test to see whether the user has selected a cell in our predefined range (so the code doesn't needlessly run when other cells are selected).
Option Explicit
Sub Worksheet_SelectionChange(ByVal Target As Range)
'Only run the code if the user selected a cell in our defined range:
If Not Intersect(Target, Me.Range("C3:H152")) Is Nothing Then
'Declare variables
Dim rInt As Range
Dim rCell As Range
Dim rw As Long
Dim xLoc As Range
Set rInt = Me.Range(Me.Cells(Target.Row, "C"), Me.Cells(Target.Row, "H"))
If Not rInt Is Nothing Then
'Look for a response in our answer range
Set xLoc = rInt.Find("x")
If Not xLoc Is Nothing Then
'If there was a response and the response was in the same column _
'we selected, wipe the response and exit the sub.
If Target.Column = xLoc.Column Then
rInt.Value = vbNullString
Exit Sub
'Else, wipe the previous response and add the new response
Else
rInt.Value = vbNullString
Target.Value = "x"
End If
'If there were no previous responses...
Else: Target.Value = "x"
End If
End If
End If
End Sub

All you need here are radio buttons that are linked to a cell and then edit the click code.
Private Sub OptionButton1_Click()
Range("D3:H3") = False
Range("C3") = True
End Sub
Private Sub OptionButton2_Click()
Range("C3") = False
Range("D3") = True
Range("E3:H3") = False
End Sub
Then format the cells to make the text the same color as the background and use conditional formatting to change the color of the cell the radio button is in front of by referencing the TRUE or FALSE, very nice for the user and easy.
I'm guessing you haven't used them before so just so you know, you go to the developer tab, I always use the ActiveX radio buttons. Then use design mode to edit the properties of the option button, and change "GroupName" to link any the radio buttons together where you only want one to be clicked by naming them with the same group name.

Related

Message box to take the row number when multiple errors are shown

I am using this code to receive an error message every time when in column "W" a text is inserted. When this happens the text is deleted and a box message appears:"The row W" & r & " must contain only digits!" which tells the row number of the error. r - is set as Target.Row
My problem is that, when I copy a text in the range w10:w12, I receive the error message 3 times, which is great. But, in the message box it shows only row number w10 - 3 times i.e."The row W10 must contain only digits!" . How can I make the code to show the message box with w10, then w11 and lastly then w12?
Private Sub Worksheet_Change(ByVal Target As Range) Dim cell As Range
Dim r As Long
r = Target.Row
Application.EnableEvents = False
For Each cell In Target
If Not Application.Intersect(cell, Range("w10:w10000")) Is Nothing Then
If Not IsNumeric(cell.Value) Then
MsgBox "The row W" & r & " must contain only digits!"
cell.Value = vbNullString
End If
End If
Next cell
Application.EnableEvents = True
[...] to receive an error message every time when in column "W" a text is inserted. When this happens the text is deleted and a box message appears:"The row W" & r & " must contain only digits!"
The right thing to do here, is to use Data Validation so as to restrict the possible values a cell can take.
You can specify an error message that Excel displays given an invalid value:
...and even a tooltip message when the cell is selected:
Here I've configured data validation for cell A1:
You can do all that with VBA code (using the Range.Validation API), but really there's no need at all.
Private Sub Worksheet_Change(ByVal Target As Range) Dim cell As Range
Application.EnableEvents = False
For Each cell In Target
If Not Application.Intersect(cell, Range("w10:w10000")) Is Nothing Then
If Not IsNumeric(cell.Value) Then
MsgBox "The row W" & cell.row & " must contain only digits!"
cell.Value = vbNullString
End If
End If
Next cell
Application.EnableEvents = True
It would be easier to get intersected range first and then checking those cells:
Sub F()
Dim cell As Range
Dim rngArea As Range
Dim rngIntersect As Range
Set rngIntersect = Intersect(Selection, [W10:W10000])
If rngIntersect Is Nothing Then Exit Sub
For Each rngArea In rngIntersect.Areas
For Each cell In rngArea
'// The code...
Next
Next
End Sub

populating data from userform checkboxes & optional buttons

I am creating a userform that I want to be able to populate values in a data tab as well as default to certain values.
I think I have text boxes and combo boxes down, but cannot find info on using multiple optional buttons to generate data to one cell depending on the selection.
from the example, my criteria would be "secondary insurance" how do I go about linking them all so that, lets say cell b1 is populated with the selected option?
I'm completely guessing but I think checkboxes are a little more simple, true if checked and false if unchecked.
What I have so far is just a code I came across to fill in a cell with the value of the designated text/combo box and was just going to repeat for each column I need to set a criteria for.
Private Sub CommandButton1_Click()
Dim LastRow As Long, ws As Worksheet
Set ws = Sheets("Sheet1")
LastRow = ws.Range("A" & Rows.Count).End(xlUp).Row + 1 'Finds the last blank row
ws.Range("A" & LastRow).Value = TextBox1.Text 'Adds the TextBox1 into Col A & Last Blank Row
Me.Hide
End Sub
combobox list
Private Sub UserForm_Initialize()
ComboBox1.Value = ("N/A")
ComboBox1.List = Split("N/A Yes No")
End Sub
Please let me know if I lack information and or how to attach my test worksheet, hopefully you can see the picture (I can't on my work server).
Thanks in advance for any and all education.
If the caption of the option button is the same as you want as cell text, then something like this may be what you want to store it:
Private Sub CommandButton1_Click()
Dim LastRow As Range
With Sheets("Sheet1")
Set LastRow = .Rows(.Cells(Rows.Count, 1).End(xlUp).Row + 1).Cells
If OptionButton1 Then
LastRow(2).Value2 = Me.OptionButton1.Caption
ElseIf Me.OptionButton2 Then
LastRow(2).Value2 = Me.OptionButton2.Caption
Else
LastRow(2).Value2 = Me.OptionButton3.Caption
End If
End With
End Sub
This will set the desired cell to the value of the caption of the option button you have selected.
To load the data back in the userform, you could use something like this:
Sub Load_in(Row_To_Load As Long)
Dim MyRow As Range
With Sheets("Sheet1")
Set MyRow = .Rows(Row_To_Load).Cells
If MyRow(2).Value2 = OptionButton1.Caption Then
OptionButton1.Value = True
ElseIf MyRow(2).Value2 = OptionButton2.Caption Then
OptionButton2.Value = True
Else
OptionButton3.Value = True
End If
End With
End Sub
For this, I assumed that the names hasn't been changed. Also if nothing is selected, the third option (N/A) will be used. The same goes for loading it back. If you do not want that, simply change the Else part to ElseIf so it looks like the first 2 options.

Protecting a Sheet - While allowing changes via a double click

I am creating a spreadsheet where people are to enter when something has been completed. I figured the most efficient way would be to use double click tick boxes. However, I want to pull the user ID and the timestamp for this, and don't want anyone to be able to edit anything except if they are double clicking something for the first time.
I have the below which works for what I need but I don't know how to protect the sheet exactly as I wish.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
'Limit Target count to 1
If Target.Count > 1 Then Exit Sub
'Isolate Target to a specific range
If Intersect(Target, Range("Ckboxes")) Is Nothing Then Exit Sub
'Set Target font to "marlett"
Target.Font.Name = "marlett"
'Check value of target
If Target.Value <> "a" Then
Target.Value = "a" 'Sets target Value = "a"
Target.Offset(0, 1).Value = Environ("UserName")
Target.Offset(0, 2).Value = Format(Now, "yyyy-mm-dd hh:mm:ss")
Cancel = True
Exit Sub
End If
If Target.Value = "a" Then
Cancel = True
Exit Sub
End If
End Sub
Also, if I protect columns C and D then it won't let the macro enter the values needed there. I know I may need to protect the whole worksheet and have it unlock the cells upon a double click to allow the change to happen and then lock again straight after but I can't figure out how to manage that!
Any help is appreciated!
What you could do is protect the sheet as usual and put in the check-boxes. Then assign this macro to all the checkboxes -
Sub ChkBxClk()
Dim shp As Shape
Set shp = ActiveSheet.Shapes(Application.Caller)
If shp.ControlFormat.Value = xlOff Then
MsgBox ("This was already checked off")
shp.ControlFormat.Value = xlOn
Exit Sub
End If
If shp.ControlFormat.Value = xlOn Then
ActiveSheet.Unprotect
Dim rng As Range
Set rng = Range(shp.TopLeftCell.Address)
rng.Offset(0, 1).value = Environ("UserName")
rng.Offset(0, 2).value = Format(Now, "yyyy-mm-dd hh:mm:ss")
ActiveSheet.Protect
End If
End Sub
Now, if a user checks a box (which is allowed on a protected sheet), it will unlock and allow you to enter what you want in the offset cells.

VBA Deleting buttons with a range

I'm trying to figure out how to delete all buttons within a range. I've seen plenty of examples on how to delete all buttons within a sheet but not a range. I created a range variable the contains every possible occurance of a button (this is used to reinitialize a form of variable size). The problem is that range doesnt support the object .Shapes or .Buttons.
Set totalTable = Range(ActiveCell, ActiveCell.Cells(1000, 1000))
For Each gen_btn In totalTable.Shapes
gen_btn.Delete
Next
Any help would be appreciated. Also, I can't use ActiveSheet becuase there are buttons which i want to keep and becuase the macro is called by a button. Hence the need for a range. Thank you.
This solution uses the Intersect method to see whether the shape is in your range and deletes the shape if it is.
Sub Delete_Shapes_In_Range()
Dim btn As Shape
Dim totalTable As Range
Set totalTable = Range(ActiveCell, ActiveCell.Cells(1000, 1000))
For Each btn In ActiveSheet.Shapes
If Not Intersect(btn_rng, totalTable) Is Nothing Then btn.Delete
Next btn
End Sub
Note that this code will not only delete buttons, but will also delete other shapes. If this is a concern, you can add an If statement to skip certain shapes. For example:
If Not btn.Name Like "Picture*" Then '<~~Will skip pictures
or
If Not btn.Name Like "*box*" Then '<~~Will skip textboxes
etc. This assumes that you haven't renamed the shapes since creating them.
I'll show you how to extract the "position" of a button (it's not optimal, but it works). Up to you to adapt it to make it work as it should. This will dislpay the row and column of the top-left cell touched by each button (in the ActiveSheet) in successive message boxes.
Sub Testing()
For Each butt In ActiveSheet.Buttons
MsgBox "Row : " & butt.TopLeftCell.Row & vbCrLf & "Column : " & butt.TopLeftCell.Column
Next butt
End Sub
The complete code:
Sub DeleteRangeButtons()
rng = "A1:A10" ' Place range here.
Dim btn As Button, ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
For Each btn In ws.Buttons
If isinrange(btn.TopLeftCell.Row, btn.TopLeftCell.Column, rng) Then
btn.Delete
End If
Next btn
Next ws
End Sub
Function isinrange(x, y, rng)
Cells(x, y).Activate
If Intersect(ActiveCell, Range(rng)) Is Nothing Then
isinrange = False
Else
isinrange = True
End If
End Function
Commenting on answer by ARich (which was useful to me) since I couldn't add a comment directly. It misses setting btn_rng, but btn.TopLeftCell could be used instead.
Also, I prefer
btn.Type = msoPicture
instead of
btn.Name Like "Picture.
Here is my method based on that:
Public Sub DeleteIntersectingPictures(ByVal sheetToDeleteIn As Worksheet, ByVal rangeToLookIn As range)
Dim noOfRowsInSheet As Long
Dim pictureItem As Shape
Dim pictureRange As range
For Each pictureItem In sheetToDeleteIn.Shapes
If pictureItem.Type = msoPicture Then
Set pictureRange = sheetToDeleteIn.range( _
pictureItem.TopLeftCell.Address & ":" & pictureItem.BottomRightCell.Address)
If Not Intersect(pictureRange, rangeToLookIn) Is Nothing Then
Call pictureItem.Delete
End If
End If
Next pictureItem
End Sub

VBA (Excel): Jump to (or remain) cell in column

I would like to start off with stating that I have virtually no coding experience. I found a VBA snippet online for highlighting an entire selected range (just to as a visual guide):
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
Application.ScreenUpdating = False
' Clear the color of all the cells
Cells.Interior.ColorIndex = 0
With Target
' Highlight the entire column that contain the active cell
.EntireRow.Interior.ColorIndex = 8
End With
Application.ScreenUpdating = True
End Sub
I would like to also have the cursor jump-to column "J". For instance, after performing a search for cells containing the words "strawberry topping" after pressing 'OK' the cell containing that text becomes active and, due to the VBA code, the entire row is highlighted.
The first cell I need to work on is in column "J". Can I also have column J selected along with the row being highlighted?
Thank you so much for your time and would appreciate any help you may have to offer.
My Three cents
If you are using xl2007+ then do not use Target.Cells.Count. Use Target.Cells.CountLarge else you will get an Overflow error if a user tries to select all cells by pressing CTRL + A as Target.Cells.Count can't hold a Long value.
If you want to select the row and the column, you might want to switch off events else you might end up in endless loop.
Since you are working with events, use error handling.
Is this what you are trying?
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim Rw As Long, Col As Long
Dim ColName As String
On Error GoTo Whoa
If Target.Cells.CountLarge > 1 Then Exit Sub
Application.ScreenUpdating = False
Application.EnableEvents = False
' Clear the color of all the cells
'Cells.Interior.ColorIndex = 0
With Target
Rw = .Row
Col = .Column
ColName = Split(Cells(, Col).Address, "$")(1)
' Highlight the entire column that contain the active cell
'.EntireRow.Interior.ColorIndex = 8
Range(ColName & ":" & ColName & "," & Rw & ":" & Rw).Select
End With
LetsContinue:
Application.ScreenUpdating = True
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
End Sub