Data validation doesn't update after running macro - excel

I have an follow-up question about this: Worksheet cell does not update after macro runs
The data validation is supose to mark the background in the cell red if it doesn't exist in the RoleList, this part works fine, when data gets entered before running the macro, but after running the macro if there still exist a fault in the grammer of the column were data validation should apply it doesn't mark the background red.
So if I have Moderatori in the RoleList before running the macro it marks the cell red, because in the RoleList it says it should be Moderator so thats ok, but if I put Moderatori in the cell and start the macro it doesn't apply the red background for the cell (should be false).
Data validation part:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const RolesList As String = "Moderator"
Const FirstCellAddress As String = "A2"
Const Delimiter As String = "||"
Application.ScreenUpdating = True
Application.EnableEvents = True
ActiveSheet.EnableCalculation = True
Dim rng As Range
With Range(FirstCellAddress)
Set rng = Intersect(.Resize(.Worksheet.rows.Count - .Row + 1), Target)
End With
If rng Is Nothing Then
Exit Sub
End If
Application.ScreenUpdating = True
Application.EnableEvents = False
Dim cel As Range
For Each cel In rng.Cells
cel.Value = removeTrail(cel.Value, Delimiter)
Next cel
Application.EnableEvents = True
Dim Roles() As String: Roles = Split(RolesList, ",")
Dim dRng As Range
Dim aRng As Range
Dim Curr() As String
Dim cMatch As Variant
Dim n As Long
Dim isFound As Boolean
For Each cel In rng.Cells
If Not IsError(cel) Then
Curr = Split(cel.Value, Delimiter)
For n = 0 To UBound(Curr)
cMatch = Application.Match(Curr(n), Roles, 0)
If IsError(cMatch) Then
isFound = True
Exit For
Else
If StrComp(Curr(n), Roles(cMatch - 1), _
vbBinaryCompare) <> 0 Then
isFound = True
Exit For
End If
End If
Next n
If isFound Then
isFound = False
If dRng Is Nothing Then
Set dRng = cel
Else
Set dRng = Union(dRng, cel)
End If
End If
End If
Next cel
rng.Interior.Color = xlNone
If Not dRng Is Nothing Then
dRng.Interior.Color = vbRed
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
ActiveSheet.EnableCalculation = True
Worksheets(1).Columns(12).Calculate
End Sub
Function removeTrail( _
ByVal SearchString As String, _
ByVal RemoveString As String, _
Optional ByVal doTrim As Boolean = True) _
As String
If doTrim Then
removeTrail = Trim(SearchString)
Else
removeTrail = SearchString
End If
If Right(removeTrail, Len(RemoveString)) = RemoveString Then
removeTrail = Left(removeTrail, Len(removeTrail) - Len(RemoveString))
End If
End Function
Module 3:
Option Explicit
Sub RemoveFormats()
'Remove all formatting except changes in font and font size
'Turn off screen updates to improve performance
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets("Sheet1").Cells
'Remove cell colors
.Interior.ColorIndex = xlNone
'Remove all cell borders
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Borders(xlEdgeLeft).LineStyle = xlNone
.Borders(xlEdgeTop).LineStyle = xlNone
.Borders(xlEdgeBottom).LineStyle = xlNone
.Borders(xlEdgeRight).LineStyle = xlNone
.Borders(xlInsideVertical).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
'Remove all special font properties and formatting
With .Font
.FontStyle = "Regular"
.Strikethrough = False
.Superscript = False
.Subscript = False
.Underline = xlUnderlineStyleNone
.ColorIndex = xlAutomatic
End With
End With
'Restore screen updates to display changes
Application.ScreenUpdating = True
End Sub

I removed this in Module 3: .Interior.ColorIndex = xlNone it works now, this part was ofcourse removing the background color from the cells.
I changed this part to: Range("A:K").Interior.ColorIndex = 0 (So it doesn't remove the background color of the data validation happening in column L)
Option Explicit
Sub RemoveFormats()
'Remove all formatting except changes in font and font size
'Turn off screen updates to improve performance
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets("Sheet1").Cells
'Remove cell colors
'.Interior.ColorIndex = xlNone
Range("A:K").Interior.ColorIndex = 0
'Remove all cell borders
.Borders(xlDiagonalDown).LineStyle = xlNone
.Borders(xlDiagonalUp).LineStyle = xlNone
.Borders(xlEdgeLeft).LineStyle = xlNone
.Borders(xlEdgeTop).LineStyle = xlNone
.Borders(xlEdgeBottom).LineStyle = xlNone
.Borders(xlEdgeRight).LineStyle = xlNone
.Borders(xlInsideVertical).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
'Remove all special font properties and formatting
With .Font
.FontStyle = "Regular"
.Strikethrough = False
.Superscript = False
.Subscript = False
.Underline = xlUnderlineStyleNone
.ColorIndex = xlAutomatic
End With
End With
'Restore screen updates to display changes
Application.ScreenUpdating = True
End Sub

Related

Find any part of a text string of keywords separated by spaces

I'm trying to match any part of a text string of keywords to text in the Target cell.
If it is a text value, of a single keyword, like "wal" for Wal-mart, then this code finds the text string in the Target, i.e. " WAL-MART SUPERCENTER ".
When I have "wal otherstore" in the keywords cell it doesn't find Wal-mart anymore. I thought that is what xlPart was supposed to do.
Set Found = Target.Find(What:=Cell, LookIn:=xlValues, LookAt:=xlPart, MatchCase:=False)
The idea is to have a list of accounts in a budget spreadsheet with associated keywords, so that the macro then finds keywords, like "wal" and assigns the Target entry to that account.
The keywords are all in a single cell next to the accounts list. The keywords are separated by a space.
Full code:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
Dim Rng1 As Range, Rng2 As Range, Rng3 As Range, Cell As Range, Found As Range
Dim List As String
Dim Qty As Integer
Dim Coll As Collection
Dim i As Long
LastRowA = Sheets("Transactions").Cells(Rows.Count, "A").End(xlUp).Row
LastRowL = Sheets("Transactions").Cells(Rows.Count, "L").End(xlUp).Row
List = "Reset Options"
Qty = 0
Set Coll = New Collection
Set Rng1 = Sheets("Transactions").Range("G3:G" & LastRowA)
Set Rng2 = Sheets("Accounts & Budget").Range("I5:I104")
Set Rng3 = Sheets("Transactions").Range("L3:L" & LastRowL)
If Application.Intersect(Target, Rng1) Is Nothing Then
ElseIf Application.Intersect(Target, Rng1).Address = Target.Address Then
If Target = "" Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Cells(Target.Row, "G") = ""
Cells(Target.Row, "L") = ""
Application.EnableEvents = True
ElseIf Target <> "" Then
For Each Cell In Rng2
Set Found = Target.Find(What:=Cell, LookIn:=xlValues, LookAt:=xlPart, MatchCase:=False)
If Found Is Nothing Then
Else
Qty = Qty + 1
Coll.Add Cell.Offset(0, -2)
End If
Next Cell
If Qty = 1 And Coll.Count = 1 Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Target.Offset(0, 5) = Coll(1)
Target.Offset(0, 5).Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Accounts"
Application.EnableEvents = True
ElseIf Qty = 0 And Coll.Count = 0 Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Target.Offset(0, 5) = "No Results - Select From List"
Target.Offset(0, 5).Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Accounts"
Application.EnableEvents = True
ElseIf Qty > 1 And Coll.Count > 1 Then
Application.EnableEvents = False
Target.Offset(0, 5).Validation.Delete
Target.Offset(0, 5) = "Multiple Results - Select From List"
For i = 1 To Coll.Count
List = List & "," & Coll(i)
Next i
Target.Offset(0, 5).Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:=List
Application.EnableEvents = True
End If
End If
End If
If Application.Intersect(Target, Rng3) Is Nothing Then
ElseIf Application.Intersect(Target, Rng3).Address = Target.Address Then
If Target = "Reset Options" Or Target = "" Then
Application.EnableEvents = False
Target.Validation.Delete
Target.Validation.Add Type:=xlValidateList, Operator:=xlBetween, Formula1:="=Accounts"
Application.EnableEvents = True
Target = "Select From List"
ElseIf Target.Offset(0, -5) = "" Then
Application.EnableEvents = False
Target.Validation.Delete
Target = ""
Application.EnableEvents = True
End If
End If
Application.ScreenUpdating = True
End Sub

Error 9 subscript out of range when applying conditional formatting

I want to apply some conditional formatting to my sheet, it errors out my code below
the line that errors out is
MyRange.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
do you think that i have the 2nd row hidden, may impact the method?
Sub DraxCXLImport()
Dim header As Integer
Dim r As Integer
On Error GoTo ErrorHandler
Application.EnableEvents = False
Worksheets("APM").Activate
Range("A1").CurrentRegion.Select
r = Selection.Rows.Count
If r < 3 Then
r = 3
End If
Worksheets("Cxl Policies").Activate
'drag down formulas
Worksheets("Cxl Policies").Range("A2:AA" & r).FillDown
'add conditional formatting
'Define Range
Dim MyRange As Range
Set MyRange = Worksheets("Cxl Policies").Range("S2:T" & r)
'Delete Existing Conditional Formatting from Range
MyRange.FormatConditions.Delete
'Apply Conditional Formatting to Tier cancellation hours
MyRange.FormatConditions.Add Type:=xlExpression, Formula1:="=$S3<>$T3"
MyRange.FormatConditions(Selection.FormatConditions.Count).SetFirstPriority
With Selection.FormatConditions(1).Interior
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
End With
Selection.FormatConditions(1).StopIfTrue = False
'remove duplicates
Worksheets("Cxl Policies").Range("A1:AA" & r).RemoveDuplicates Columns:=Array(1, 2), header:=xlYes
Worksheets("Cxl Policies").Rows(2).Hidden = True
Worksheets("Cxl Policies").Range("A1").Select
Application.EnableEvents = True
Exit Sub
ErrorHandler:
MsgBox Err.Number & " " & Err.Description
Err.Clear
Application.EnableEvents = True
Resume Next
End Sub
You're using both MyRange and Selection in the line that's erroring - that's probably the cause of the problem.
However, FormatConditions.Add() returns the added FormatCondition, so you can simplify your code by using the return value directly in a With block:
Dim MyRange
'...
'...
Set MyRange = Worksheets("Cxl Policies").Range("S2:T" & r)
MyRange.FormatConditions.Delete
With MyRange.FormatConditions.Add(Type:=xlExpression, Formula1:="=$S3<>$T3")
.SetFirstPriority
.StopIfTrue = False
With .Interior
.PatternColorIndex = xlAutomatic
.Color = 65535
.TintAndShade = 0
End With
End With

Multi conditions in Case to trigger cells change

I use this code to make cells change but I feel the Case is too chunky. Can anyone help to make it more neatly. Basically the following conditions trigger the change to happen :
Conditions to trigger the event
firstcell value and secondcell value
Cells values
firstcell value = ("E") or ("N")
secondcell value =("D", "D1", "D2", "D3", "D4", "D5", "G", "K")
and
firstcell value =("N")
secondcell value =("E")
Sub PairedCell3()
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.ScreenUpdating = False
Dim C As Range, rng As Range, rng1 As Range
Set rng = Range("C3", Range("AL" & Rows.Count).End(xlUp))
With rng.Borders
.LineStyle = xlContinuous
.Weight = xlThin
End With
'Make Target and Adjacent Cells Change
For Each C In rng
Case "ED", "ED1", "ED2", "ED3", "ED4", "ED5", "EG", "EK", _
"ND", "ND1", "ND2", "ND3", "ND4", "ND5", "NE", "NG", "NK"
With C.Resize(, 2)
.Borders.LineStyle = xlContinuous
.Borders.Weight = xlThick
.Borders.Color = RGB(102, 0, 255)
End With
End Select
Next C
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub

Excel VBA copy paste then format

Hi and thank you for any help with this, currently I have code that copy and pastes text from a named range then I have code that formats it however the range needs to be dynamic, I have it just set to do where my first table shows but I have over 50 tables that will be copy and pasted over:
Here is my code for the Text to be copy and pasted over:
Private Sub CommandButton1_Click()
Application.ScreenUpdating = False
Dim copySheet As Worksheet
Dim pasteSheet As Worksheet
Set copySheet = Worksheets("Auto")
Set pasteSheet = Worksheets("Final")
copySheet.Range("Range1").Copy
pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(2, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
Application.ScreenUpdating = True
End Sub
Here is my code for the Formatting:
Private Sub CommandButton2_Click()
ThisWorkbook.Worksheets("Final").Range("A1:E15").Columns.AutoFit
ThisWorkbook.Worksheets("Final").Range("A3:E3").Interior.Color = RGB(180, 198, 231)
ThisWorkbook.Worksheets("Final").Range("A19:D19").Merge
ThisWorkbook.Worksheets("Final").Range("A4:A18").Merge
ThisWorkbook.Worksheets("Final").Range("A4:A17").HorizontalAlignment = -4131
ThisWorkbook.Worksheets("Final").Range("A4:A17").VerticalAlignment = -4160
ThisWorkbook.Worksheets("Final").Range("A19:D19").Interior.ColorIndex = 48
ThisWorkbook.Worksheets("Final").Range("A3:E19").Borders.LineStyle = xlContinuous
ThisWorkbook.Worksheets("Final").Range("A3:E19").Borders.Color = vbBlack
ThisWorkbook.Worksheets("Final").Range("A3:E19").Borders.Weight = xlThin
ThisWorkbook.Worksheets("Final").Range("A3:E3").Font.Bold = True
ThisWorkbook.Worksheets("Final").Range("D4:E18", "E19").Style = "Currency"
ThisWorkbook.Worksheets("Final").Range("E19").Font.Bold = True
End Sub
As you can see the code for the formatting is not dynamic but static, how would I make this dynamic, or how would I go about implementing this formatting into the text code so that it copys and pasted the text across and then formats it?
End result should look like:
Use a combination of Offset and Resize
Option Explicit
Private Sub CommandButton1_Click()
Application.ScreenUpdating = False
Dim copySheet As Worksheet, pasteSheet As Worksheet
Dim ar, r, rng As Range
ar = Array("Range1", "Range2", "Range3")
Set copySheet = Worksheets("Auto")
Set pasteSheet = Worksheets("Final")
For Each r In ar
Set rng = pasteSheet.Cells(Rows.Count, 1).End(xlUp).Offset(2, 0)
copySheet.Range(r).Copy rng
Call ApplyFormat(rng)
Next
Application.ScreenUpdating = True
End Sub
Private Sub ApplyFormat(ByVal rng As Range)
Set rng = rng.Cells(1, 1) ' top left corner
rng.Resize(15, 5).Columns.AutoFit ' A1:E15
With rng.Offset(2, 0).Resize(1, 5) ' A3:E3
.Interior.Color = RGB(180, 198, 231)
.Font.Bold = True
End With
With rng.Offset(18, 0).Resize(1, 4) ' A19:D19
.Merge
.Interior.ColorIndex = 48
End With
With rng.Offset(3, 0).Resize(15, 1) ' A4:A18
.Merge
.HorizontalAlignment = -4131
.VerticalAlignment = -4160
End With
With rng.Offset(2, 0).Resize(17, 5).Borders ' A3:E19
.LineStyle = xlContinuous
.Color = vbBlack
.Weight = xlThin
End With
rng.Offset(3, 3).Resize(15, 2).Style = "Currency" ' D4:E18
With rng.Offset(18, 4) ' E19
.Style = "Currency"
.Font.Bold = True
End With
End Sub

Hide/Unhide Excel Sheets based on multiple cell values

I have an Excel workbook which contains multiple sheets. I want to hide/unhide sheets based on cell values in Main sheet cells B3:B8. Values in Main sheet are changed by the user from pre-defined list.
Eg. If "A" exists in the "Config" column, then unhide sheet "A" in my workbook.
At the moment I have following code, which works, but looks
clunky, Excel flickers as the code runs every time a value is changed in "Config" column:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
Sheets("A").Visible = False
Sheets("B").Visible = False
Sheets("C").Visible = False
Sheets("D").Visible = False
For i = 3 To 8
If InStr(1, Cells(i, 2), "A") Then
Sheets("A").Visible = True
ElseIf InStr(1, Cells(i, 2), "B") Then
Sheets("B").Visible = True
ElseIf InStr(1, Cells(i, 2), "C") Then
Sheets("C").Visible = True
ElseIf InStr(1, Cells(i, 2), "D") Then
Sheets("D").Visible = True
End If
Next i
End Sub
I also tried to run this macro from a button, but it stops with first TRUE value (a sheet becomes unhidden).
I would use this method:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
Sheets("A").Visible = xlSheetHidden
Sheets("B").Visible = xlSheetHidden
Sheets("C").Visible = xlSheetHidden
Sheets("D").Visible = xlSheetHidden
Application.ScreenUpdating = False
For i = 3 To 8
If InStr(1, Cells(i, 2), "A") Then Sheets("A").Visible = xlSheetVisible
If InStr(1, Cells(i, 2), "B") Then Sheets("B").Visible = xlSheetVisible
If InStr(1, Cells(i, 2), "C") Then Sheets("C").Visible = xlSheetVisible
If InStr(1, Cells(i, 2), "D") Then Sheets("D").Visible = xlSheetVisible
Next i
Application.ScreenUpdating = True
End Sub
Another way to do this would be:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim RNG As Range, CL As Range
Dim WS As Worksheet
Application.ScreenUpdating = False
Set RNG = Sheets("Main").Range("B3:B8")
If Not Intersect(Target, RNG) Is Nothing Then
Application.ScreenUpdating = False
For Each WS In ThisWorkbook.Worksheets
If WS.Name <> "Main" Then
With RNG
Set CL = .Find(What:=WS.Name, LookIn:=xlValues, LookAt:=xlWhole)
If Not CL Is Nothing Then
WS.Visible = xlSheetVisible
Else
WS.Visible = xlSheetHidden
End If
End With
End If
Next WS
End If
Application.ScreenUpdating = True
End Sub
More versatile and more dynamic
EDIT: To also check if Target intersects with your lookup range to prevent triggering macro unwanted.
To help optimize the running and have it look better use Application.ScreenUpdating. It will reduce the flickering by not trying to repaint the scrren until the Sub has finished running. If the rest of the program runs with no issue it should be all you need
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
Sheets("A").Visible = False
Sheets("B").Visible = False
Sheets("C").Visible = False
Sheets("D").Visible = False
For i = 3 To 8
If InStr(1, Cells(i, 2), "A") Then
Application.ScreenUpdating = False
Sheets("A").Visible = True
ElseIf InStr(1, Cells(i, 2), "B") Then
Application.ScreenUpdating = False
Sheets("B").Visible = True
ElseIf InStr(1, Cells(i, 2), "C") Then
Application.ScreenUpdating = False
Sheets("C").Visible = True
Application.ScreenUpdating = False
ElseIf InStr(1, Cells(i, 2), "D") Then
Sheets("D").Visible = True
End If
Next i
Application.sScreenUpdating = True
End Sub
I also agree with 's comment. Ifs would be better. ElseIf assumes only one condition is the correct one when there could be multiple iterations.
edit:
Also a though: It looks like the way its set up you intend that any value between B3:B8 that has an "A" will show page "A". If you dedicate it differently B3 = "A" , B4="B" etc and so on, you can change the conditionals to If Target.Address = "$B$3" Then and have B# be the on/off to sheet"A" with any non-empty value.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
If Target.Address = "$B$3" Then
If IsEmpty(Sheet1.Range("B3")) = False Then
Sheets("A").Visible = True
Else
Sheets("A").Visible = False
End If
End If
''etc etc and so on
Application.ScreenUpdating = True
End Sub

Resources