My workbook contains several sheets, each with multiple checkboxes. All checkboxes in all worksheets have the linked cell in row 80. In a worksheet called "Info" I am using countif to count the total number of times the text "TRUE" occurs in row(s) 80 for all worksheets. The total is in Info!B8.
I need to call a macro each time cell Info!b8 changes. So in other words; every time a checkbox is clicked, the linked cell changes, cell Info!b8 goes up or down and I need a macro to run.
This is the code I am using, but it doesn't do anything. I have researched this and from what I can tell it should work??
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$8" Then
Call CreateFinalWorksheet
End If
End Sub
Assuming all your CheckBoxes are of Form Controls, with a bit of altering the CheckBox Creation, you can achieve what you want without the need of LinkedCell and CountIfs etc.
Example: CreateCheckBoxes() below will create a check box for each cell in Range("D1:D5"), Name it with a prefix and the cell address, assigns the Sub CheckBoxClicked when clicked.
In Sub CheckBoxClicked(), it will go through all worksheets (except named "Info"), then increment a counter if the value of checkbox named ending D3 is 1 (ticked). After that, if threshold is met, it calls the Sub CreateFinalWorksheet().
Option Explicit
Private Const ChkBoxPrefix As String = "cbx_"
Private Const ThresholdToCreateFinalWorksheet As Long = 3
Sub CreateChkBoxes()
Dim myCBX As CheckBox, c As Range
For Each c In Range("D1:D5") 'rngCB
With c
Set myCBX = ActiveSheet.CheckBoxes.Add(Top:=.Top, Width:=.Width, Height:=.Height, Left:=.Left)
End With
With myCBX
.Name = ChkBoxPrefix & c.Address(0, 0)
.Caption = "Check Box " & c.Address(0, 0) 'strCap
.OnAction = "CheckBoxClicked" ' "CheckBox_Click"
End With
Next c
End Sub
Sub CheckBoxClicked() ' CheckBox_Click()
Dim oWS As Worksheet, lChecked As Long
On Error Resume Next ' Just in case the named CheckBox does not exist
lChecked = 0
For Each oWS In ThisWorkbook.Worksheets
If oWS.Name <> "Info" Then
' If you need to keep track of more than 1 checkbox in each worksheet, go through them
' If you need all of them to be checked before CreateFinalWorksheet, exit when a checkbox.value = 0
With oWS.CheckBoxes(ChkBoxPrefix & "D3") ' <-- Change to what you need to keep track of
lChecked = lChecked + IIf(.Value = 1, 1, 0)
End With
End If
Next
On Error GoTo 0
If lChecked >= ThresholdToCreateFinalWorksheet Then CreateFinalWorksheet
End Sub
Private Sub CreateFinalWorksheet()
Debug.Print "CreateFinalWorksheet()"
End Sub
Alternatively you put the event Sub Worksheet_Calculate() into Info module, and check if the Info!B8 is large enough to call CreateFinalWorksheet.
Related
I am trying to hide every "A" column in my workbook when a certain checkbox in my user form is not selected.
I have 6 worksheets in my one workbook.
I have 6 checkboxes.
When a checkbox is not selected, I'd like to hide the column that it's associated with.
Ex: When the "Advice" checkbox is not checked, I'd like to hide the "A" column in EVERY worksheet in my workbook.
Thank you!
I tried this:
shtFinancial.Range("D").EntireColumn.Hidden = Not cbAdvice.Value
And this:
If cbAdvice.Value = True Then
shtFinancial.Range("D").EntireColumn.Hidden
Checkboxes in VBA are really funky. Here is one method:
Sub cbAdvice_Click()
Dim CheckBox As Shape, ws As Worksheet
Set CheckBox = Sheet1.Shapes("cbAdvice")
For Each ws In ActiveWorkbook.Worksheets
If CheckBox.OLEFormat.Object.Value = 1 Then
ws.Columns("A:A").EntireColumn.Hidden = False
Else
ws.Columns("A:A").EntireColumn.Hidden = True
End If
Next ws
End Sub
I created two active x control boxes "CheckboxColA" and "CheckboxColB" and gave them the following code for a change of state:
Sheet1("Worksheet Controls")
Private Sub CheckboxColA_Change()
Call changeColAVisiblity(CheckboxColA.Value, "A")
End Sub
Private Sub CheckBoxColB_Change()
Call changeColAVisiblity(CheckBoxColB.Value, "B")
End Sub
Then created the following code in "module1"
Sub changeColAVisiblity( _
cbState As Boolean, _
changeColumn As String)
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
'If statement Assumes sheet with contols should not change
If ws.Name <> "CheckBox Controls" Then
ws.Columns(changeColumn).Hidden = cbState
End If
Next
End Sub
I have created a userform which has a listbox (ListBox1)which list down the sheet names and I can double click on the list and it will take me to that sheet. I have a back button (CommandButton2) when I click on back button it will take me to the previous selected sheet.
I want to link my list box and back button so that when I click on back button the my listbox (ListBox1)should highlight the sheet where my back button (CommandButton2) has directed to.
Please find below my codes:
Private Sub UserForm_Initialize()
Dim Sh As Worksheet
'for each loop the add visible sheets
For Each Sh In ActiveWorkbook.Sheets
'add sheets to the listbox
Me.ListBox1.AddItem Sh.Name
Next Sh
End Sub
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
'declare the variables
' modifed code for ListBox double-click event, store the sheet name before switching to the selected item
Dim i As Long
LastSelectedSht = ActiveSheet.Name ' <-- save the current active sheet before selecting a new one
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then
Worksheets(ListBox1.List(i)).Activate
End If
Next i
End Sub
Private Sub CommandButton2_Click()
Dim TmpSht As String
TmpSht = ActiveSheet.Name ' <-- save the current active sheet
' select the previous sheet (stored in LastSelectedSht)
If LastSelectedSht = "" Then
MsgBox "Error, no sheet stored , is it your first time running ? "
Else
Sheets(LastSelectedSht).Select
End If
LastSelectedSht = TmpSht ' <-- use the temp variable to store the latest active sheet
' reset the userform
Unload Me
frmNavigation.Show
End Sub
No need for al those loops to seek selected item, you can simplify things a bit as follows:
Option Explicit
Dim LastSelectedSht As String '<--| use as UserForm scoped variable to store the name of "last" sheet selected
Private Sub UserForm_Initialize()
Dim Sh As Worksheet
With Me.ListBox1
'for each loop the add visible sheets
For Each Sh In ActiveWorkbook.Sheets
.AddItem Sh.Name 'add sheets names to the listbox
Next Sh
LastSelectedSht = ActiveSheet.Name ' <-- store the currently active sheet name as the "last" one
.Value = LastSelectedSht '<--| initialize listbox selection with the currently active sheet name
End With
End Sub
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
' modifed code for ListBox double-click event, store the sheet name before switching to the selected item
LastSelectedSht = ActiveSheet.Name
Worksheets(ListBox1.Value).Activate '<--| activate the sheet whose name has been dblclicked
End Sub
Private Sub CommandButton2_Click()
Sheets(LastSelectedSht).Activate
Me.ListBox1.Value = LastSelectedSht
' reset the userform
Unload Me
frmNavigation.Show
End Sub
This sub will change the selected item in the list box.
Private Sub SetListBox(Lbx As MSForms.ListBox, _
Itm As String)
Dim i As Integer
With Lbx
For i = 0 To .ListCount - 1
.Selected(i) = Not CBool(StrComp(.List(i), Itm))
Next i
End With
End Sub
Call it from your procedure which activates the previous worksheet with a line of code like this one.
SetListBox ListBox1, TmpSht
Make sure that TmpSht holds the name of the newly activated sheet at the time you make the call or pass the name of that sheet instead of TmpSht.
I have created a userform frmNavigation which has a ListBox1, which will list down all the worksheets present in my workbook and I can double click on any of worksheet listed in the listbox and go to that sheet.
Now as I have close to 50 worksheets so I double click from the list appearing in ListBox1 and go to that sheet but now I want a back button "CommandButton2" so that it can take me back to my previous active sheet.
I have created a code but its not working.
Private Sub CommandButton2_Click()
Application.ScreenUpdating = False
Dim i As Integer, Sht As String
'for loop
For i = 0 To ListBox1.ListCount - 1
'get the name of the selected sheet
If ListBox1.Selected(i) = True Then
Sht = ListBox1.List(i - 1)
End If
Next i
'select the sheet
Sheets(Sht).Select
'reset the userform
Unload Me
frmNavigation.Show
End Sub
Try the code below, I am not sure how to explain my logic of the code below, I tired my best to describe it in the code comments.
I've modified also the ListBox1_DblClick code event, to save the latest ActiveSheet before you Select the new sheet.
Code
Option Explicit
Dim LastSelectedSht As String ' Variable at module level, to store the name of the last selected sheet
'===================================================================
Private Sub CommandButton2_Click()
Dim TmpSht As String
TmpSht = ActiveSheet.Name ' <-- save the current active sheet
' select the previous sheet (stored in LastSelectedSht)
If LastSelectedSht = "" Then
MsgBox "Error, no sheet stored , is it your first time running ? "
Else
Sheets(LastSelectedSht).Select
End If
LastSelectedSht = TmpSht ' <-- use the temp variable to store the latest active sheet
' reset the userform
Unload Me
frmNavigation.Show
End Sub
'===================================================================
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
' modifed code for ListBox double-click event, store the sheet name before switching to the selected item
Dim i As Long
LastSelectedSht = ActiveSheet.Name ' <-- save the current active sheet before selecting a new one
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) Then
Worksheets(ListBox1.List(i)).Activate
End If
Next i
End Sub
'=================================================================
Private Sub UserForm_Activate()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Sheets
Me.ListBox1.AddItem ws.Name
Next ws
End Sub
I am attempting to write a bit of Visual Basic code to prevent anyone from accidentally overwriting cells across multiple sheets when multiple sheets are selected.
I do however want the option of overwriting cells across multiple sheets, should that be required at any stage.
So, when I have multiple sheets selected I would like a pop up with 2 options, as follows:
"Are you sure you want to overwrite the cells across the sheets you have selected?"
Ok Cancel
I think I am nearly there with the code below, but if I have 3 sheets selected then the pop up will appear 3 times (once for each page). Naturally I only want the pop up to appear once regardless of how many sheets I have selected.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If ActiveWindow.SelectedSheets.Count > 1 Then
If MsgBox("Are you sure you want to overwrite the cells across the sheets you have selected?", vbOKCancel) = vbCancel Then Exit Sub
Application.EnableEvents = False
Application.Undo
End If
Application.EnableEvents = True
End Sub
Or an even better solution would actually be:
"Are you sure you want to overwrite the cells across the sheets you have selected?"
Yes (to continue with all selected pages),
No (to select current page and continue),
Cancel (to cancel operation and keep current selection).
This solution validates if the event worksheet is the active worksheet in order to fire the Multiple Selection procedure.
Also if user chooses to update only the active sheet, the procedure leaves all other sheets included in the selection as they were before the action that triggered the vent, instead of the undesired effect of entering in all those cell the vbNullString value
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Application.EnableEvents = False
If Sh.Name = ActiveSheet.Name Then Call Wsh_MultipleSelection(Target)
Application.EnableEvents = True
End Sub
Private Sub Wsh_MultipleSelection(ByVal rTrg As Range)
Const kTtl As String = "Selection Across Multiple Sheets"
Const kMsg As String = "You are trying to overwrite cells across multiple sheets." & vbLf & _
"Press [Yes] if you want to continue and overwrite the selected cells" & vbLf & _
"Press [No] if you want to overwrite selected cells in active sheet only" & vbLf & _
"Press [Cancel] to undo last action."
Const kBtt As Long = vbApplicationModal + vbQuestion + vbYesNoCancel + vbDefaultButton3
Dim iResp As Integer
Dim vCllVal As Variant
Dim bWshCnt As Byte
bWshCnt = ActiveWindow.SelectedSheets.Count
If bWshCnt > 1 Then
bWshCnt = -1 + bWshCnt
iResp = MsgBox(kMsg, kBtt, kTtl)
Select Case iResp
Case vbYes
Rem NO ACTION!
Case vbNo:
Rem Select Only Active Sheet
vCllVal = rTrg.Cells(1).Value2
Application.Undo
rTrg.Value = vCllVal
Case Else
Rem Cancel
Application.Undo
End Select: End If
End Sub
This is very tricky, since by using the Workbook_SheetChange event the code will fire for every instance of a sheet change which you have to account for.
However, with some crafty use of public variables to use as a switch / counter and a separate sub-routine to process which cases to change all vs. active vs. no worksheets, I have developed code that has been thoroughly tested. I have also heavily commented my code to help understand the logic.
Option Explicit
Dim bAsked As Boolean
Dim dRet As Double
Dim iCnt As Long
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Application.EnableEvents = False
Dim lSheets As Long
lSheets = ActiveWindow.SelectedSheets.Count
If lSheets > 1 Then Check lSheets, Sh, Target
Application.EnableEvents = True
End Sub
Sub Check(iTotal As Long, ws As Worksheet, rng As Range)
'use this is a counter to count how many times the sub has been called in the firing of the 'Workbook_SheetChange` event
iCnt = iCnt + 1
'if the question has not been asked yet (first time event is fired)
If Not bAsked Then
dRet = MsgBox("Are you sure you want to overwrite the cells across the sheets you have selected? Click Yes to overwrite all sheets, No to overwrite the Active Sheet, or Cancel to abort the entire overwrite.", vbYesNoCancel)
bAsked = True 'set to true so question will only be asked once on event firing
End If
'dRet will always be the same for each instance an event is fired
Select Case dRet
Case Is = vbYes
'set the value for each range to what user entered
ws.Range(rng.Address) = rng.Value2
Case Is = vbNo
'only set the value the user entered to the active worksheet (the one the user is on)
If ActiveSheet.Name = ws.Name Then
ws.Range(rng.Address) = rng.Value2
Else
ws.Range(rng.Address) = vbNullString
End If
Case Is = vbCancel
'do not set any values on any sheet
Application.Undo
End Select
'if the total times the sub has been called is equal to the total selected worksheet reset variables so they work next time
'if the count equals the total it's the last time the sub was called which means its the last sheet
If iCnt = iTotal Then
bAsked = False
iCnt = 0
End If
End Sub
I currently run a macro to compare the most recent sheet of data to the report immediately prior and highlight changes. It works fine on its own. Now, however, we would like to be able to compare selected sheets from any time period. My idea was to pop up a simple userform with two textboxes that the user can use to specify which two reports he wants to compare. I am quite lost though with the idea of trying to declare public variables; what I've got atm is:
Option Explicit
Public shtNew As String, shtOld As String, _
TextBox1 As TextBox, TextBox2 As TextBox
Sub SComparison()
Const ID_COL As Integer = 31 'ID is in this column
Const NUM_COLS As Integer = 31 'how many columns are being compared?
Dim rwNew As Range, rwOld As Range, f As Range
Dim X As Integer, Id
shtNew = CSManager.TextBox1
shtOld = CSManager.TextBox2
'Row location of the first employee on "CurrentMaster" sheet
Set rwNew = shtNew.Rows(5)
Do While rwNew.Cells(ID_COL).Value <> ""
Id = rwNew.Cells(ID_COL).Value
Set f = shtOld.UsedRange.Columns(ID_COL).Find(Id, , xlValues, xlWhole)
If Not f Is Nothing Then
Set rwOld = f.EntireRow
For X = 1 To NUM_COLS
If rwNew.Cells(X).Value <> rwOld.Cells(X).Value Then
rwNew.Cells(X).Interior.Color = vbYellow
rwNew.Cells(33) = "UPDATE"
Else
rwNew.Cells(X).Interior.ColorIndex = xlNone
End If
Next X
End If
Set rwNew = rwNew.Offset(1, 0) 'next row to compare
Loop
Call SUpdates
End Sub
My Suggestion would be to use Comboboxes instead of TextBoxes. Create a userform with two command buttons and two comboboxes and populate the comboboxes in the UserForm_Initialize() event using this code.
Private Sub UserForm_Initialize()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
ComboBox1.AddItem ws.Name: ComboBox2.AddItem ws.Name
Next
End Sub
And then use this code in the OK button to do the comparison.
Private Sub CommandButton1_Click()
Dim shtNew As Worksheet, shtOld As Worksheet
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select the first sheet"
Exit Sub
End If
If ComboBox2.ListIndex = -1 Then
MsgBox "Please select the Second sheet"
Exit Sub
End If
Set shtNew = Sheets(ComboBox1.Value)
Set shtOld = Sheets(ComboBox2.Value)
'~~> REST OF THE CODE HERE NOW TO WORK WITH THE ABOVE SHEETS
End Sub
Private Sub CommandButton2_Click()
Unload Me
End Sub
HTH
Sid
For an easy fix, couldn't you just colour (sorry, I'm English!) the worksheets that you want to refer to, then do something like:
Sub ListSheets()
'lists only non-coloured sheets in immediate window
'(could amend to add to combo boxes)
Dim w As Worksheet
'loop over worksheets in active workbook
For Each w In Worksheets
If w.Tab.Color Then
'if tab color is set, print
Debug.Print w.Name
End If
Next w
Let me know if this solves your problem.