Excel VBA Macro Help - Mandatory Cells - excel

New user, was referred to your helpful website by a friendly team member.
Problem: Trying to force a user in excel to fill in a cell in a column (column O) before filling in a cell in columns I-L. The problem lies in that not every cell in the columns needs to be filled in. I've found a VBA code that has somewhat helped but the problem is the pop up will still occur if column O is filled before there is text in just one of the cells in column I-L (and therefore the error occurs unless all 4 cells in the row are filled in). As mentioned, the goal is (for example) to get O264 to be filled in first before any of the cells in column I,J,K or L264 are filled in.
Further exacerbating this issue is there are multiple rows I need this applied to, believe this is where the range fits in. However, playing with the range line in excel does not work in the way I've tried.
Code below:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("I:L")) Is Nothing Then
If Target.Cells.Count > 1 Or IsEmpty(Target) Then Exit Sub
If Target.Offset(, -1).Value = "" Then
MsgBox "You must first enter feedback in column ""O"""
Target.Value = ""
Target.Offset(, -1).Select
End If
End If
End Sub

This could be a case where you might need to aid the user a little more. You could do that by hiding the dependent cells, by locking them, by greying them out, etc. My feeling is that displaying a message box whenever a user enters data in the wrong order is a little too reactive.
In the example below, the target cells are locked and greyed until something is entered in column 'O'. You'd also need to create a list of target rows if you have more than one.
In your code behind the appropriate sheet, the skeleton code below should get you started. I've included a couple of helper functions to make the code a little clearer for you:
Option Explicit
Private Const SHEET_PASSWORD As String = "xyz" 'whatever password you choose.
Private Const TARGET_ROWS As String = "2,4,6" 'your target rows, separated by commas.
Private Const TARGET_COLUMN As String = "O"
Private Const DEPENDENT_COLUMNS As String = "I:L"
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, cell As Range
Set rng = Intersect(Target, Me.Columns(TARGET_COLUMN))
'Exit routine if we're not in the target column.
If rng Is Nothing Then Exit Sub
'Process the target column cells.
For Each cell In rng.Cells
If IsTargetRow(cell.Row) Then
SetDependentStates cell
End If
Next
End Sub
Private Sub SetDependentStates(cell As Range)
Dim DependentRange As Range
'Define the Dependent range based on passed cell row.
Set DependentRange = Intersect( _
cell.EntireRow, _
Me.Range(DEPENDENT_COLUMNS) _
)
'Lock/unlock and paint Dependent rows, based on
'contents of passed cell.
Application.EnableEvents = False 'prevent capture of change event.
Me.Unprotect SHEET_PASSWORD
With DependentRange.Cells
If Len(cell.Value) = 0 Then
.ClearContents
.Locked = True
With .Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorDark1
.TintAndShade = -0.249977111117893
.PatternTintAndShade = 0
End With
Else
.Locked = False
With .Interior
.Pattern = xlNone
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End If
End With
Me.Protect SHEET_PASSWORD
Me.EnableSelection = xlUnlockedCells
Application.EnableEvents = True
End Sub
Private Function IsTargetRow(rowNum As Long) As Boolean
Dim v As Variant
'Tests if the pass row number is in the target row list.
For Each v In Split(TARGET_ROWS, ",")
If CLng(v) = rowNum Then
IsTargetRow = True
Exit Function
End If
Next
End Function
Public Sub InitialiseDependentStates()
Dim v As Variant
Dim cell As Range
'Define your unlocked cells.
'This is a simple example, adjust as you wish.
With Me
.Unprotect SHEET_PASSWORD
.Cells.Locked = False
.Protect SHEET_PASSWORD
.EnableSelection = xlUnlockedCells
End With
For Each v In Split(TARGET_ROWS, ",")
Set cell = Me.Range(TARGET_COLUMN & v)
SetDependentStates cell
Next
End Sub
You'll likely want to initialise the dependent states when the workbook is opened. Do this in the code behind the Workbook:
Private Sub Workbook_Open()
Sheet1.InitialiseDependentStates 'use whichever sheet you're using.
End Sub

Related

Use value in cell as range in macro

I want to use the value in cell as a dynamic way to change the range in my macro, more specifically when changing the colour of a cell.
Lets says CELL A1 hosts the desirable range data. The macro will always look at this cell. The text in A1 = 'B4' therefore I want this to be the selected cell to have a colour fill. This will be repeated quite a few times and the data will change, this is why i need it to be a dynamic macro not conditional formatting.
Below is the standard colour macro and is far as i can get, VBA noob sorry.
Sub Colour2()
'
' Colour2 Macro
'
'
Range("B4").Select
With Selection.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = 16731903
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End Sub
Hope this makes sense and thanks for your understanding and help.
M
Highlight Range Supplied in a Cell
This is an automated solution. You run nothing. Just copy the codes in the appropriate modules.
As you change the range address in cell A1, the color is applied to the cells of the supplied range address and the range is selected.
Sheet object module e.g. Sheet1 (the name not in parentheses in the VBE Project Explorer)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const mAddress As String = "A1"
Const ColorValue As Long = 16731903
Dim mCell As Range: Set mCell = Range(mAddress)
If Not Intersect(mCell, Target) Is Nothing Then
On Error Resume Next
Dim rg As Range: Set rg = Range(mCell.Value)
On Error GoTo 0
If Not rg Is Nothing Then
Application.ScreenUpdating = False
' Removes the colors from all cells before applying the color.
applyColor rg, ColorValue, True
' Applies the color without removing the previously applied colors.
'applyColor rg, ColorValue
rg.Select
Application.ScreenUpdating = True
End If
End If
End Sub
Standard Module e.g. Module1 (optionally in the same sheet object module)
Sub applyColor( _
ByVal rg As Range, _
ByVal ColorValue As Long, _
Optional ByVal resetBeforeApply As Boolean = False)
If resetBeforeApply Then
rg.Worksheet.Cells.Interior.Color = xlNone
End If
With rg.Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.Color = ColorValue
.TintAndShade = 0
.PatternTintAndShade = 0
End With
End Sub
The standard module is preferred because it could then easily be used for other worksheets.

Use Excel VBA to update column cell contents

This is a simple answer but one I cannot find.
I have two columns of data.
Column A (1) has yes/no data validation list options in every cell.
Column B also has data validation with say 6 strings of text options in every cell.
But I only want each the corresponding cell (column B) to update in the same row as column A
e.g A20 toggled, then B20 is updated. Like so
A20 is selected “Yes” from the dropdown option and B20 is updated with the string “complete” which is one of the states you can select in the dropdown boxes manually in every cell in column B.
I had some code but I would have to write an argument for every cell and then two macros for every yes / no.
This is code that works for one cell only but this is not ideal for many cells but it works
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A20")) Is Nothing Then
Select Case Range("A20")
Case "Yes": Macro_001
Case "No": Macro_002
End Select
End If
End Sub
Sub Macro_001()
Application.Calculation = xlManual
Application.ScreenUpdating = False
Sheets("August 2020").Select
Sheets("August 2020").Range("B20").Select
ActiveCell.FormulaR1C1 = "Complete"
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
Sub Macro_002()
Application.Calculation = xlManual
Application.ScreenUpdating = False
Sheets("August 2020").Select
Sheets("August 2020").Range("B20").Select
ActiveCell.FormulaR1C1 = ""
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
There much be an easier way with .range perhaps
Thanks in advance
In the developer tab click view code, choose the sheet you want the macro to run on, make sure the upper left drop down says worksheet and the upper right says Change (I'll assume your sheet has headers):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Dim ChangeCell As Range
Dim numrows As Long
numrows = Cells(Rows.Count, 1).End(xlUp).Row
Set TriggerCells = Range("A1")
Set KeyCells = Range("B2:B" & numrows)
If Application.Intersect(TriggerCells, Range("A1")) = "[Column Header Text]" Then
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
If Target.Value = "Yes" Then
Range("B" & Target.Row).Value = "Completed"
End If
End If
End If
End Sub
Try that, see if it works for you.

VBA Range find in 1 column to Show/Hide another column

This one has driven me mad so hopefully one of you can tell me what I'm doing wrong.
All I want to do is if any cell in column I contains the value "Y" then column J gets hidden. If you then delete the Y from any cell then column J appears again.
Here's my code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Range("I:I")
Set cell = rng.Find(What:="Y")
If cell Is Nothing Then
Columns("J").EntireColumn.Hidden = True
Else
Columns("J").EntireColumn.Hidden = False
End If
End Sub
Basically the code does absolutely nothing. Which annoys me more than getting an error, because I have no clue what I've done wrong.
Try the following...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cell As Range
Columns("I:I").Select
Set cell = Selection.Find(What:="Y", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If cell Is Nothing Then
Columns("J").EntireColumn.Hidden = False
Else
Columns("J").EntireColumn.Hidden = True
End If
End Sub
This is something that works:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
If Target.Column = 9 Then
With Range("I:I")
Set c = .Find("Y")
If Not c Is Nothing Then
Columns("J").EntireColumn.Hidden = True
Else
Columns("J").EntireColumn.Hidden = False
End If
End With
End If
End Sub
In order to make sure that it works, put your code in the corresponding sheet,not in the module
Make sure that macros are enabled
Firstly, you can keep this VBA really simple if you have a cell in your workbook which tests if there are any Y in column I. I put the following into cell A1
=COUNTIF(I:I,"Y")
I advise putting this on the worksheet because Excel is smart and will perform this calculation quicker than VBA - it also gives you some sight of what's going on.
We can now use the value of A1 to determine whether to hide column J in the macro
Firstly, it's probably a good idea to test if column J is in the correct state when you open the workbook so put the following code into the ThisWorkbook module. This code is optional
Sub Workbook_Open()
With Me.Worksheets("Sheet1")
If .Range("A1").Value > 0 Then
.Range("J:J").EntireColumn.Hidden = True
Else:
.Range("J:J").EntireColumn.Hidden = False
End If
End With
End Sub
Then to monitor any changes to values in column I, place the following code into your worksheet module
Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("I:I")) Is Nothing Then
If Me.Range("A1") > 0 Then
Me.Range("J:J").EntireColumn.Hidden = True
Else
Me.Range("J:J").EntireColumn.Hidden = False
End If
End If
End Sub
Note: this code is not designed for the user changing multiple cells at once, which shouldn't be a problem if you intend to use it as described. If you wish to paste over multiple cells (for example) at a time, then this will need to be adapted slightly.
Private Sub Worksheet_Change(ByVal Target As Range)
Columns("J").EntireColumn.Hidden = WorksheetFunction.CountIf(Columns("I"),"Y") >0
End Sub
Or
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Columns("I")) Is Nothing Then Columns("J").EntireColumn.Hidden = WorksheetFunction.CountIf(Columns("I"),"Y") >0
End Sub

how to keep initial cell color, while code is hilighting active row

I have this code for workbook:
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
'toggles worksheet colors
'code will remove all color
'and color active row and column
If ActiveCell.Interior.ColorIndex <> xlNone Then
Cells.Interior.ColorIndex = xlNone
Else
Cells.Interior.ColorIndex = xlNone
ActiveCell.EntireRow.Interior.ColorIndex = 4
End If
End Sub
and works good. but if a row has an initial color, it will be removed. let me know how
active row will be highlighted and by changing the row, will get its initial color?
Damn, I couldn't find the Add-In but I recreated the code for you. Please note that this is not thoroughly tested. In whatever small tests that I did, it works...
Logic:
Create a hidden sheet.
Store the current cell's formats in row 1 of that hidden sheet
Store the currently selected row number in active sheet to cell A2 of the hidden sheet
when you move to a different row then retrieve the last row number and restore it.
Code:
In thisWorkbook code area
Private Sub Workbook_Open()
Dim ws As Worksheet
'~~> Delete the Temp sheet we created i.e if we created
Application.DisplayAlerts = False
On Error Resume Next
Sheets("MyHiddenSheet").Delete
On Error GoTo 0
Application.DisplayAlerts = True
'~~> ReCreate the Sheet
Set ws = ThisWorkbook.Sheets.Add
'~~> i am using a normal name. Chnage as applicable
ws.Name = "MyHiddenSheet"
'~~> Hide the sheet
ws.Visible = xlSheetVeryHidden
End Sub
In relevant sheet code area. I am using Sheet1 as an example
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'~~> Don't do anything if multiple cells are selected
If Target.Cells.CountLarge > 1 Then Exit Sub
Dim ws As Worksheet
'~~> Set our relevant sheet
Set ws = ThisWorkbook.Sheets("MyHiddenSheet")
'~~> Get the row number of the last row we had selected earlier
'~~> For obvious reasons, this will be empty for the first use.
If Len(Trim(ws.Cells(2, 1).Value)) <> 0 Then
'~~> If user has moved to another row then
'~~> Restor the old row
If Target.Row <> Val(ws.Cells(2, 1).Value) Then
ws.Rows(1).Copy
Rows(ws.Cells(2, 1).Value).PasteSpecial xlFormats
End If
End If
'~~> Copy the current row's format to the hidden sheet
Rows(Target.Row).Copy
ws.Rows(1).PasteSpecial xlFormats
'~~> Store the current rows value in cell A2
ws.Cells(2, 1).Value = Target.Row
'~~> Highlight the current row in a shade of blue.
'~~> Chnage as applicable
With Rows(Target.Row).Interior
.Pattern = xlSolid
.PatternColorIndex = xlAutomatic
.ThemeColor = xlThemeColorAccent5
.TintAndShade = 0.799981688894314
.PatternTintAndShade = 0
Rows(Target.Row).Select
End With
'~~> Remove the `Ants` which appear after you do a copy
Application.CutCopyMode = False
End Sub
Screenshots:
Here's an alternate approach which makes use of the fact that Excel always "overlays" Conditional Formatting on top of whatever formatting is already on the sheet.
Define a worksheet-level name "ROWNUM" and assign a value of 0.
Add a conditional format using the formula =(ROW()=ROWNUM) and add whatever formatting you want to use for row highlighting.
Your SelectionChange sub is then just:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Me.Names("ROWNUM").RefersToR1C1 = "=" & Target.Cells(1).Row
End Sub

Excel Data Validation -Disable cell in excel based on drop down value

I have 2 columns in an Excel sheet. A1 contains a drop down with the values "Enable" and "Disable". Cell B1 and C1 needs to be enabled or disabled based on the A1 drop down selection.
I tried using Data Validation but it does not work. Can anyone suggest how I might use Data Validation to accomplish this?
A | B
------------------------
1 Suggestions| (This cell should disable (B1))
------------------------
2 Errors| (Now here drop down will come with values(B2))
------------------------
Current VBA
Private Sub Worksheet_Change(ByVal Target As Range)
ThisRow = Target.Row
If Target = Range("A1") Then
If Target.Value = "Suggestions" Then
Worksheets("Code Review").Range("B:C").Locked = True
End If
Else
Worksheets("Code Review").Range("B:C").Locked = False
End If
End Sub
Also tried like this
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cel As Range
If Not Intersect(Range("A:A"), Target) Is Nothing Then
Application.EnableEvents = False
Me.Unprotect
For Each cel In Intersect(Range("A:A"), Target)
cel.Offset(ColumnOffset:=1).Resize(ColumnSize:=2).Locked = _
cel.Value = "Suggestion"
Next cel
Me.Protect
Application.EnableEvents = True
End If
But this locks everything :( I want just 2 cells to be locked.

Resources