I am trying to write a VBA code such that a Message box pops up whenever the value inside certain cells change.
The cells that I want to monitor change because they are linked to a query which is refreshed automatically. The VBA codes I found online only work when the cell value is changed manually, this will not work because the formula in the cells do not change, only the displayed value changes.
Can anyone help me with this?
Application Calculate (Public Variables)
The following is written for a non-contiguous range.
A message box pop's up each time a value in the range changes via formula.
Copy the codes into the appropriate modules.
Adjust the values in the constants section.
This is an automated solution. To start you should either save, close, and reopen the workbook or run the popupMsgBoxInit procedure.
Standard Module e.g. Module1
Option Explicit
Public Const popupWsName As String = "Sheet1"
Public Const popupRgAddress As String = "A1,C3,E5"
Public popupRg As Range
Public popupCount As Long
Public popupArr As Variant
Sub popupMsgBoxInit()
Set popupRg = ThisWorkbook.Worksheets(popupWsName).Range(popupRgAddress)
popupRg.Interior.Color = 65535 'xlNone
popupCount = popupRg.Cells.Count
ReDim popupArr(1 To popupCount)
Dim cel As Range
Dim i As Long
For Each cel In popupRg.Cells
i = i + 1
popupArr(i) = cel.Value
Next cel
End Sub
Sub popupMsgBox()
Dim chCount As Long
Dim cel As Range
Dim i As Long
For Each cel In popupRg.Cells
i = i + 1
If cel.Value <> popupArr(i) Then
chCount = chCount + 1
popupArr(i) = cel.Value
End If
Next cel
MsgBox "Number of Changes: " & chCount, vbInformation, "Success"
End Sub
ThisWorkbook Module
Option Explicit
Private Sub Workbook_Open()
popupMsgBoxInit
End Sub
Sheet Module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Calculate()
popupMsgBox
End Sub
Related
I'm going crazy trying to find a way for code to run when I click on ANY of the checkboxes on my sheet. I've seen multiple articles talking about making a class module, but I can't seem to get it to work.
I have code that will populate column B to match column C. Whatever I manually type into C10 will populate into B10, even if C10 is a formula: =D9. So, I can type TRUE into D10 and the formula in C10 will result in: TRUE and then the code populates B10 to say: TRUE. Awesome... the trick is to have a checkbox linked to D10. When I click the checkbox, D10 says TRUE and the formula in C10 says TRUE, but that is as far as it goes. The VBA code does not recognize the checkbox click. If I then click on the sheet (selection change), then the code will run, so I know I need a different event.
It is easy enough to change the event to "Checkbox1_Click()", but I want it to work for ANY checkbox I click. I'm not having ANY luck after days of searching and trying different things.
here is the code I'm running so far
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim i As Long
For i = 3 To 11
Range("B" & i).Value = Range("c" & i)
Next i
End Sub
Any help would be appreciated.
this works
' this goes into sheet code
Private Sub Worksheet_Activate()
activateCheckBoxes
End Sub
.
' put all this code in class a module and name the class module "ChkClass"
Option Explicit
Public WithEvents ChkBoxGroup As MSForms.CheckBox
Private Sub ChkBoxGroup_Change()
Debug.Print "ChkBoxGroup_Change"
End Sub
Private Sub ChkBoxGroup_Click()
Debug.Print "ChkBoxGroup_Click"; vbTab;
Debug.Print ChkBoxGroup.Caption; vbTab; ChkBoxGroup.Value
ChkBoxGroup.TopLeftCell.Offset(0, 2) = ChkBoxGroup.Value
End Sub
.
' this code goes into a module
Option Explicit
Dim CheckBoxes() As New ChkClass
Const numChkBoxes = 20
'
Sub doCheckBoxes()
makeCheckBoxes
activateCheckBoxes
End Sub
Sub makeCheckBoxes() ' creates a column of checkBoxes
Dim sht As Worksheet
Set sht = ActiveSheet
Dim i As Integer
For i = 1 To sht.Shapes.Count
' Debug.Print sht.Shapes(1).Properties
sht.Shapes(1).Delete
DoEvents
Next i
Dim xSize As Integer: xSize = 2 ' horizontal size (number of cells)
Dim ySize As Integer: ySize = 1 ' vertical size
Dim t As Range
Set t = sht.Range("b2").Resize(ySize, xSize)
For i = 1 To numChkBoxes
sht.Shapes.AddOLEObject ClassType:="Forms.CheckBox.1", Left:=t.Left, Top:=t.Top, Width:=t.Width - 2, Height:=t.Height
DoEvents
Set t = t.Offset(ySize)
Next i
End Sub
Sub activateCheckBoxes() ' assigns all checkBoxes on worksheet to ChkClass.ChkBoxGroup
Dim sht As Worksheet
Set sht = ActiveSheet
ReDim CheckBoxes(1 To 1)
Dim i As Integer
For i = 1 To sht.Shapes.Count
ReDim Preserve CheckBoxes(1 To i)
Set CheckBoxes(i).ChkBoxGroup = sht.Shapes(i).OLEFormat.Object.Object
Next i
End Sub
All you need is to let EVERY checkbox's _Click() event know that you want to run the Worksheet_SelectionChange event. To do so you need to add the following line into every _Click() sub:
Call Worksheet_SelectionChange(Range("a1"))
Please note that it is irrelevant what range is passed to the SelectionChange sub since you do not use the Target in your code.
I'm going crazy trying to find a way for code to run when I click on ANY of the checkboxes on my sheet. I've seen multiple articles talking about making a class module, but I can't seem to get it to work.
I have code that will populate column B to match column C. Whatever I manually type into C10 will populate into B10, even if C10 is a formula: =D9. So, I can type TRUE into D10 and the formula in C10 will result in: TRUE and then the code populates B10 to say: TRUE. Awesome... the trick is to have a checkbox linked to D10. When I click the checkbox, D10 says TRUE and the formula in C10 says TRUE, but that is as far as it goes. The VBA code does not recognize the checkbox click. If I then click on the sheet (selection change), then the code will run, so I know I need a different event.
It is easy enough to change the event to "Checkbox1_Click()", but I want it to work for ANY checkbox I click. I'm not having ANY luck after days of searching and trying different things.
here is the code I'm running so far
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim i As Long
For i = 3 To 11
Range("B" & i).Value = Range("c" & i)
Next i
End Sub
Any help would be appreciated.
this works
' this goes into sheet code
Private Sub Worksheet_Activate()
activateCheckBoxes
End Sub
.
' put all this code in class a module and name the class module "ChkClass"
Option Explicit
Public WithEvents ChkBoxGroup As MSForms.CheckBox
Private Sub ChkBoxGroup_Change()
Debug.Print "ChkBoxGroup_Change"
End Sub
Private Sub ChkBoxGroup_Click()
Debug.Print "ChkBoxGroup_Click"; vbTab;
Debug.Print ChkBoxGroup.Caption; vbTab; ChkBoxGroup.Value
ChkBoxGroup.TopLeftCell.Offset(0, 2) = ChkBoxGroup.Value
End Sub
.
' this code goes into a module
Option Explicit
Dim CheckBoxes() As New ChkClass
Const numChkBoxes = 20
'
Sub doCheckBoxes()
makeCheckBoxes
activateCheckBoxes
End Sub
Sub makeCheckBoxes() ' creates a column of checkBoxes
Dim sht As Worksheet
Set sht = ActiveSheet
Dim i As Integer
For i = 1 To sht.Shapes.Count
' Debug.Print sht.Shapes(1).Properties
sht.Shapes(1).Delete
DoEvents
Next i
Dim xSize As Integer: xSize = 2 ' horizontal size (number of cells)
Dim ySize As Integer: ySize = 1 ' vertical size
Dim t As Range
Set t = sht.Range("b2").Resize(ySize, xSize)
For i = 1 To numChkBoxes
sht.Shapes.AddOLEObject ClassType:="Forms.CheckBox.1", Left:=t.Left, Top:=t.Top, Width:=t.Width - 2, Height:=t.Height
DoEvents
Set t = t.Offset(ySize)
Next i
End Sub
Sub activateCheckBoxes() ' assigns all checkBoxes on worksheet to ChkClass.ChkBoxGroup
Dim sht As Worksheet
Set sht = ActiveSheet
ReDim CheckBoxes(1 To 1)
Dim i As Integer
For i = 1 To sht.Shapes.Count
ReDim Preserve CheckBoxes(1 To i)
Set CheckBoxes(i).ChkBoxGroup = sht.Shapes(i).OLEFormat.Object.Object
Next i
End Sub
All you need is to let EVERY checkbox's _Click() event know that you want to run the Worksheet_SelectionChange event. To do so you need to add the following line into every _Click() sub:
Call Worksheet_SelectionChange(Range("a1"))
Please note that it is irrelevant what range is passed to the SelectionChange sub since you do not use the Target in your code.
I have written the following macro that calls a Private Sub Worksheet_Calculate() if I confirm that I want to run it. Please find below the first part of the code:
Sub test()
Dim result As VbMsgBoxResult
result = MsgBox("Run Macro?", vbYesNo, "Excel VBA")
If result = vbYes Then
Call Worksheet_Calculate
End If
End Sub
The second code, which is Private Sub Worksheet_Calculate(), shall copy the result of the calculation performed in B2 and paste it in C2 as well as add a timestamp to D2. Columns C and D are populated as values in B2 change, so that I get a list of results and timestamps in C3 and D3, C4 and D4 etc. Here is the code:
Private Sub Worksheet_Calculate()
Dim lastrow As Long
lastrow = Worksheets(1).Cells(Rows.Count, 2).End(xlUp).Row
With Worksheets(1).Cells(lastrow, 2)
.Offset(1, 0) = Cells(2, 1).Value
.Offset(1, 1) = FormatDateTime(Now, vbLongTime)
End With
End Sub
The problem that I am facing is related to the fact that my Public Sub Worksheet_Calculate() is called only once and whenever I recalculate the value of B2 nothing happens.
Is there a way to a) keep second code activated or b) have a button(s) or a tickbox that would activate/deactivate the second code?
Hope it makes sense, thanks in advance!
Worksheet Calculate Event
This is what you put into Module1, or what ever you will call your Module. This is a normal module created with Add Module. I've left the name of the test program intact, but I changed the other because it is used for worksheet events, particularly the Worksheet Calculate event. The test program and the variable blnCalculation enable or disable the whole thing, so in the beginning nothing will be happening before you enable it. You can create a button for it and in its Click event just add Module1.test or just run it from Macros.
Option Explicit
Public TargetValue As Variant
Public blnCalculation As Boolean
Sub test()
Dim result As VbMsgBoxResult
result = MsgBox("Run Macro?", vbYesNo, "Excel VBA")
If result = vbYes Then
blnCalculation = True
ActiveSheet.Calculate
Else
blnCalculation = False
End If
End Sub
Sub SheetCalculate()
Dim lastrow As Long
With ActiveSheet
lastrow = .Cells(Rows.Count, 2).End(xlUp).Row
With .Cells(lastrow, 2)
.Offset(1, 0) = .Parent.Cells(2, 1).Value
.Offset(1, 1) = FormatDateTime(Now, vbLongTime)
End With
End With
End Sub
And the following is what you put into each sheet code aka Object module, which you get by double-clicking on it in VBE. The Worksheet Calculate event happens each time Excel 'decides' to calculate a worksheet, which is in your case probably guaranteed because you say that there is a formula in B2, so the event will happen every time B2 gets calculated. But it won't run the SheetCalculate program unless the value has changed which is checked with the TargetValue variable.
The Worksheet Activate event happens on a worksheet e.g. each time when you select it in the tab. In this case it is used to pass the new B2 value from the newly selected worksheet to the TargetValue variable.
Option Explicit
Private Sub Worksheet_Activate()
If Module1.blnCalculation Then _
Module1.TargetValue = Me.Range("B2").Value
End Sub
Private Sub Worksheet_Calculate()
If Module1.blnCalculation Then
If Me.Range("B2").Value <> Module1.TargetValue Then
Module1.SheetCalculate
End If
End If
End Sub
I've been working on this for some time now and have hit a real stumbling block.
I have a set of values that are available via a validated dropdown menu in Sheet 3, Column D. Once selected this value currently displays in a different sheet (Sheet 7) using excel function ='Sheet 3'!D4 and so on, and I have some code that reads this and performs an IF statement to produce a value in another cell.
My problem is the code is dependant on reading the value and not the formula.
I currently have a worksheet change command for a separate function I want to run, is there a way for this to run a second function and call any changes from sheet 3 column D into sheet 8 column D and then run my other change function?
Sheet 7 Code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
If Intersect(Target, Range("D2:D102")) Is Nothing Then Exit Sub
Application.EnableEvents = False
On Error GoTo Finalize
For Each c In Target.Cells
Select Case c.Column
Case 4
Call Print_Quality(c)
End Select
Next c
Finalize:
Application.EnableEvents = True
End Sub
Sheet 7 Module:
Sub Print_Quality(c As Range)
Dim PrintQuality As String
Dim PrintSpeed As String
PrintQuality = c.Value
If PrintQuality = "A Quality 1" Then PrintSpeed = "100"
c.Offset(0, 5).Value = PrintSpeed
End Sub
I've been trying this route but to no avail:
Worksheet 3 code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("D4:D104")) Is Nothing Then Exit Sub
Application.EnableEvents = False
On Error GoTo Finalize
UpdateVal
Finalize:
Application.EnableEvent = True
End Sub
Module:
Sub UpdateVal()
Worksheets("Sheet 7").Range("D2").Value = Worksheets("Sheet 3").Range("D4").Value
End Sub
Many thanks
Sods law, I've managed to fix this an hour after my desperation post.
I completed another worksheet change from the sheet it was calling from (Sheet 3)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("D4:D104")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
Call UpdateVal
End If
End Sub
then added this function into the module
Sub UpdateVal()
Sheet8.Cells(2, 4).Value = Sheet3.Cells(4, 4)
End Sub
this now references the value of the dropdowns in sheet 8 and allows other functionality to continue using the cell value
Have you tried stepping through your code to see where it is having an issue? It not, I would suggest putting a break at the beginning of each module, and then use F8 to step through. This will confirm it is running as it should.
You should also be fully-qualifying your references to worksheets. While presumably the sheet references should carry through given that they are in worksheet modules, there is the chance of failure. You can simply assign a variable to hold the worksheet like so:
Dim wb as Workbook
Dim ws as Worksheet
Set wb = ThisWorkbook
Set ws = wb.Sheets("YourSheetName")
Additionally, your worksheet 3 code:
EnableEvent = True
Should be:
EnableEvents = True
I have a list with background colors in "A" column and value in their ceil is the name of colors.
I want to do that when I select a cell with a background color this will change the value of "C1" value to the value that have in "A" column.
(this is not the my real name of the colors, I have a specific name for each colors.)
Like vlookup but with background colors and in the same ceil.
For example:
Thank you!
Put this in the code section of the worksheet :
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
With Target
If dictColours.Exists(.Interior.ColorIndex) Then
Sheets("Sheet1").Range("C1").Value = dictColours(.Interior.ColorIndex)
End If
End With
End Sub
And add this to a new module, replacing the sheet reference:
Public dictColours As Scripting.Dictionary
Sub test()
Set dictColours = New Scripting.Dictionary
Dim rngTarget As Range
Set rngTarget = Sheets("Sheet1").Range("A1")
Do While rngTarget.Value <> ""
dictColours.Add rngTarget.Interior.ColorIndex, rngTarget.Value
Set rngTarget = rngTarget.Offset(1, 0)
Loop
End Sub
Think of using the conditional formatting.
elaborating on the very fine solution form Will I'd propose the following alternative code to be entirely put in the code section of the relevant worksheet
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim dictColours As Scripting.Dictionary
Set dictColours = GetDictColours(Target.Parent)
With Target
If dictColours.Exists(.Interior.ColorIndex) Then
.Parent.Range("C1").Value = dictColours(.Interior.ColorIndex)
End If
End With
End Sub
Function GetDictColours(sht As Worksheet) As Scripting.Dictionary
Dim i As Long
Set GetDictColours = New Scripting.Dictionary
Do While sht.Range("A1").Offset(i) <> ""
GetDictColours.Add sht.Range("A1").Offset(i).Interior.ColorIndex, sht.Range("A1").Offset(i).Value
i = i + 1
Loop
End Function
aside from some stylistic choices (everyone has his own favorites), it should be more simple for the OP to handle, he being (as he himself stated) a total VBA beginner!