When I remove data, code will stuck - vba

I have excel sheet with dropdown list and when I choose anything from the list
macro will vlookup for requested value. But when I want to remove values from those cells, that I select them and press delete, it will show me "#N/A" and the excel is frozen, I cant do anything. Could you advise me, how can I avoid it, please?
Option Explicit
Private Sub Worksheet_Change()
Dim Target As Range
Dim selectedNa As Integer, selectedNum As Integer
selectedNa = Target.Value
If Target.Column = 10 Then
selectedNum = Application.VLookup(selectedNa, ActiveSheet.Range("dropdown"), 2, False)
If Not IsError(selectedNum) Then
Target.Value = selectedNum
Else: Exit Sub
End If
Else: Exit Sub
End If
End Sub

Try the following:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim selectedNa As Long, selectedNum As Variant
If Target.Column = 10 And Not IsEmpty(Target) Then 'selectedNa <> vbNullString Then '
Application.EnableEvents = False
On Error GoTo errhand
selectedNa = Target.Value
selectedNum = Application.VLookup(selectedNa, ActiveSheet.Range("dropdown"), 2, False)
If Not IsError(selectedNum) Then
Target.Value = selectedNum
End If
Application.EnableEvents = True
End If
Exit Sub
errhand:
If Err.Number <> 0 Then
Application.EnableEvents = True
End If
End Sub

Change your posted code to
Option Explicit
Private Sub Worksheet_Change(ByVal target As Range)
Dim selectedNa As Integer, selectedNum As Integer
On Error GoTo EH
Application.EnableEvents = False
selectedNa = target.Value
If target.Column = 10 Then
selectedNum = Application.VLookup(selectedNa, ActiveSheet.Range("dropdown"), 2, False)
If Not IsError(selectedNum) Then
target.Value = selectedNum
End If
End If
EH:
Application.EnableEvents = True
Debug.Print Err.Number, Err.Description
End Sub
The code HAS TO be put into the sheet module.
Have a look at the immediate window after you have changed or deleted a value in your sheet.

It looks like this is what you want, based on the information provided:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim CheckCells As Range
Dim ChangedCell As Range
Set CheckCells = Intersect(Me.Columns(10), Target)
Application.EnableEvents = False
If Not CheckCells Is Nothing Then
For Each ChangedCell In CheckCells.Cells
If Len(ChangedCell.Value) > 0 And WorksheetFunction.CountIf(Me.Range("dropdown"), ChangedCell.Value) > 0 Then
ChangedCell.Value = WorksheetFunction.VLookup(ChangedCell.Value, Me.Range("dropdown").Resize(, 2), 2, False)
End If
Next ChangedCell
End If
Application.EnableEvents = True
End Sub

Related

VBA Macro triggering too often

My worksheet is set up with data validation dropdowns and I am wanting a macro to ONLY trigger when the value of the cell is changed from another value in the dropdown, not from the default "empty" value.
Here is what I am trying to use:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 5 Then
If IsEmpty(Target.Value) = True Then
MsgBox "Test1"
Else
MsgBox "Test2"
End If
End If
exitHandler:
Application.EnableEvents = True
Exit Sub
End Sub
My problem is that this "IsEmpty" command is reading the cell AFTER the selection not before. I want it to read what the cells value was BEFORE the selection not after.
How can I do this?
Example approach:
Const COL_CHECK As Long = 5
Private oldVal
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
Set c = Target.Cells(1) '<< in case multiple cells are changed...
If c.Column = COL_CHECK Then
If oldVal <> "" Then
Debug.Print "changed from non-blank"
Else
Debug.Print "changed from blank"
End If
End If
exitHandler:
Application.EnableEvents = True
Exit Sub
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim c As Range
Set c = Target.Cells(1)
oldVal = IIf(c.Column = COL_CHECK, c.Value, "")
Debug.Print "oldVal=" & oldVal
End Sub
Another approach:
This will need one cell per validation-dropdown:
Function ValChange(Cell2Follow As Range) As String
ValChange = ""
If Len(Application.Caller.Text) = 0 Then Exit Function
If Application.Caller.Text = Cell2Follow.Text Then Exit Function
MsgBox "value of the cell is changed from another value in the dropdown" & vbLf & "not from the default 'empty' value"
End Function
in a different cell, assumed the dropdown is in E6:
=E6&ValChange(E6)
application.caller.text will be the old value
(calculation must be automatic)

Writing to multiple cells using If Target.Address = " " Then

I have basic code that allows the values written to this cell to be summed while maintaining the cumulative value. So if I were to type "4" into the cell, and then type "10" into the cell, the result would be "14" (not just the second value entered of "10"). Here is what I have and I must say that it works.
#
Option Explicit
Dim oldvalue As Double
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If Target.Address = "$J$5" Then
On Error GoTo fixit
Application.EnableEvents = False
If Target.Value = 0 Then oldvalue = 0
Target.Value = 1 * Target.Value + oldvalue
oldvalue = Target.Value
fixit:
Application.EnableEvents = True
End If
End Sub
#
However, I want to apply this treatment to more than just cell J5. Say for example, I want the same code logic applied to cell R5 as well.
Thur far I have tried using
OR
and I have also tried using
If Not Intersect (Target, Range("J5:R5")) Is Nothing Then
But each of these approaches ties the two cells together (so that what I enter into one gets summed into both - each cell is summing values added to the other). I can't figure it out to save my life, so took to this forum in the hopes of finding someone smarter than me.
Maybe (this is assuming existing logic is correct....not sure why you set old value to 0 if Target = 0 and what value the *1 adds?)
Option Explicit
Dim oldvalueJ As Double
Dim oldValueR As Double
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
On Error GoTo fixit
Application.EnableEvents = False
Select Case Target.Address
Case "$J$5"
If Target = 0 Then oldvalueJ = 0
Target = Target + oldvalueJ
oldvalueJ = Target
Case "$R$5"
If Target = 0 Then oldValueR = 0
Target = Target + oldValueR
oldValueR = Target
End Select
fixit:
Application.EnableEvents = True
End Sub
This is a bit more dynamic by allowing you to add unlimited cells; it also validates user input
Standard Module
Option Explicit 'Generic Module
Public Const WS1_MEM_RNG = "C5,J5,R5" 'Sheet1 memory cells
Public prevWs1Vals As Object
Public Sub SetPreviousWS1Vals()
Dim c As Range
For Each c In Sheet1.Range(WS1_MEM_RNG).Cells
If Len(c.Value2) > 0 Then prevWs1Vals(c.Address) = c.Value2
Next
End Sub
Sheet1 Module
Option Explicit 'Sheet1 Module
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If Target.CountLarge = 1 Then
If Not Intersect(Target, Me.Range(WS1_MEM_RNG)) Is Nothing Then GetPrevious Target
End If
End Sub
Private Sub GetPrevious(ByVal cel As Range)
Dim adr As String: adr = cel.Address
Application.EnableEvents = False
If Not IsError(cel.Value) And IsNumeric(cel.Value2) And Not IsNull(cel.Value) Then
If IsDate(cel.Value) Then
cel.NumberFormat = "General"
cel.Value2 = prevWs1Vals(adr)
Else
If cel.Value2 = 0 Then prevWs1Vals(adr) = 0
cel.Value2 = cel.Value2 + prevWs1Vals(adr)
prevWs1Vals(adr) = cel.Value2
End If
Else
cel.Value2 = prevWs1Vals(adr)
End If
Application.EnableEvents = True
End Sub
ThisWorkbook Module
Option Explicit 'ThisWorkbook Module
Private Sub Workbook_Open()
If prevWs1Vals Is Nothing Then Set prevWs1Vals = CreateObject("Scripting.Dictionary")
SetPreviousWS1Vals
End Sub
It can also enforce positives only
use commas to separate ranges, and add a Worksheet_SelectionChange() event to record the last user selected cell
Option Explicit
Dim oldvalue As String
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
If Intersect(Target, Range("J5,R5")) Is Nothing Then Exit Sub
If Target.Value = 0 Then Exit Sub
On Error GoTo fixit
Application.EnableEvents = False
Target.Value = Target.Value + oldvalue
fixit:
Application.EnableEvents = True
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.CountLarge = 1 Then oldvalue = Target.Value
End Sub

How Hide columns according to a cell value

I'm looking for hidding columns according to a cell value.
For exemple when the value is 1 the I to BV columns have to be hide. When value is 2 O to BV columns have to be hidding but the I to O columns have to be visible.
My code works only for 1 and I don't find how can I do...
Thank you for your help
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 1 Then
Columns("I:BV").EntireColumn.Hidden = True
Else: Columns("I:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change2(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 2 Then
Columns("O:BV").EntireColumn.Hidden = True
Else: Columns("O:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change3(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 4 Then
Columns("U:BV").EntireColumn.Hidden = True
Else: Columns("U:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change4(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 5 Then
Columns("AA:BV").EntireColumn.Hidden = True
Else: Columns("AA:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change5(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 6 Then
Columns("AG:BV").EntireColumn.Hidden = True
Else: Columns("AG:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change6(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 7 Then
Columns("AM:BV").EntireColumn.Hidden = True
Else: Columns("AM:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change7(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 8 Then
Columns("AS:BV").EntireColumn.Hidden = True
Else: Columns("AS:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change8(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 9 Then
Columns("AY:BV").EntireColumn.Hidden = True
Else: Columns("AY:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change9(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 10 Then
Columns("BE:BV").EntireColumn.Hidden = True
Else: Columns("BE:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change10(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 11 Then
Columns("BK:BV").EntireColumn.Hidden = True
Else: Columns("BK:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Private Sub Worksheet_Change11(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
If Target = 12 Then
Columns("BQ:BV").EntireColumn.Hidden = True
Else: Columns("BQ:BV").EntireColumn.Hidden = False
End If
End If
End Sub
Just calculate whether the column number is greater than the last column which you want to be visible:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
Dim i As Long
Dim lastVisible As Long
'Use cell B2 in the calculation, just in case Target is
' something like A1:D17
lastVisible = 2 + Range("B2").Value * 6
'That formula is calculating lastVisible such that:
'If B2 is 1, lastVisible will be 8 (i.e. column H)
'If B2 is 2, lastVisible will be 14 (i.e. column N)
'If B2 is 3, lastVisible will be 20 (i.e. column T)
'If B2 is 4, lastVisible will be 26 (i.e. column Z)
'... etc, up to
'If B2 is 11, lastVisible will be 68 (i.e. column BP)
'If B2 is 12, lastVisible will be 74 (i.e. column BV)
For i = 3 To 74
Columns(i).Hidden = i > lastVisible
Next
End If
End Sub
One change event with multiple conditions tests within an IF statement, using ElseIf. Without writing it all for you, the following it the structure and key elements. There are plenty of examples on stack overflow to help.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
'Code to unhide all columns goes here.
'Then test the contents of B2
If Target = 1 Then
Columns("I:BV").EntireColumn.Hidden = True
ElseIf Target = 2 Then
Columns("O:BV").EntireColumn.Hidden = True
ElseIf Target = 3 Then ......'Continue with rest of conditions
End If
End If
End Sub
You can do this in fewer lines:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim primaryCols As Range
If Not Intersect(Target, Range("B2")) Is Nothing Then
Range(Columns(3 + Range("B2").Value * 6), Columns(74)).EntireColumn.Hidden = False
Range(Columns(9), Columns(2 + Range("B2").Value * 6)).EntireColumn.Hidden = True
End If
End Sub
Basically it uses a little arithmetic to get your start column for those you want visible, and end column for those to hide, from column I onward.

VBA - Change color of modified text

I have this code that changes the color of the text in a cell if it is modified. However I was looking into something that only changes the color of modified text inside the cell. For example I have in cell A1 = "This cell" and when I change it to "This cell - this is new text" I would like just to change the color of "- this is new text"
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
If Target.Font.ColorIndex = 3 Then
Target.Font.ColorIndex = 5
Else
Target.Font.ColorIndex = 3
End If
End If
End Sub
Thanks
Here's what I put together:
Dim oldString$, newString$
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
newString = Target.Value
If Target.Font.ColorIndex = 3 Then
Target.Font.ColorIndex = 5
Else
Target.Font.ColorIndex = 3
End If
End If
Debug.Print "New text: " & newString
color_New_Text oldString, newString, Target
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range)
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
oldString$ = Target.Value
Debug.Print "Original text: " & oldString$
End If
End Sub
Sub color_New_Text(ByVal oldString As String, ByVal newString As String, ByVal theCell As Range)
Dim oldLen&, newLen&, i&, k&
oldLen = Len(oldString)
newLen = Len(newString)
Debug.Print newString & ", " & oldString
For i = 1 To newLen
If Mid(newString, i, 1) <> Mid(oldString, i, 1) Then
Debug.Print "different"
Debug.Print theCell.Characters(i, 1).Text
If theCell.Characters(i, 1).Font.ColorIndex = 3 Then
theCell.Characters(i, 1).Font.ColorIndex = 5
Else
theCell.Characters(i, 1).Font.ColorIndex = 3
End If
End If
Next i
End Sub
It's two global variables, a Worksheet_SelectionChangeand Worksheet_Changeto get the strings.
It is laborious:
detect that a cell has changed in the range of interest
use UnDo to get the original contents
use ReDo to get the new contents
compare them to get the changed characters
use the Characters property of the cell to format the new characters
I would use UnDo to avoid keeping a static copy of each of the 100 cells.
using the tip from Gary's Student, I retain the old value of cell and compare with the new value. Then use the lenght to get the 'difference' and color the 'characters'. Here's the modification:
Option Explicit
Public oldValue As Variant
Public Sub Worksheet_SelectionChange(ByVal Target As Range)
oldValue = Target.Value
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim oldColor
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
If Target.Value <> oldValue Then
oldColor = Target.Font.ColorIndex
Target.Characters(Len(oldValue) + 1, Len(Target) - Len(oldValue)).Font.ColorIndex = IIf(oldColor = 3, 5, 3)
End If
End If
End Sub
P.S. Sorry my english
This will change the font, but it's not perfect. Seems if you have different font colours in the same cell then Target.Font.ColorIndex returns NULL so it only works on the first change.
Option Explicit
Dim sOldValue As String
Private Sub Worksheet_Change(ByVal Target As Range)
Dim sNewValue As String
Dim sDifference As String
Dim lStart As Long
Dim lLength As Long
Dim lColorIndex As Long
On Error GoTo ERROR_HANDLER
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
sNewValue = Target.Value
sDifference = Replace(sNewValue, sOldValue, "")
lStart = InStr(sNewValue, sDifference)
lLength = Len(sDifference)
If Target.Font.ColorIndex = 3 Then
lColorIndex = 5
Else
lColorIndex = 3
End If
Target.Characters(Start:=lStart, Length:=lLength).Font.ColorIndex = lColorIndex
End If
On Error GoTo 0
Exit Sub
ERROR_HANDLER:
Select Case Err.Number
'I haven't added error handling - trap any errors here.
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure Sheet1.Worksheet_Change."
End Select
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
sOldValue = Target.Value
End If
End Sub
Edit: It will only work with a continuous string. Maybe can change to look at each character in sOldValue and sNewValue and change colour as required.
Try with below
Private Sub Worksheet_Change(ByVal Target As Range)
Dim newvalue As String
Dim olvalue As String
Dim content
Application.EnableEvents = False
If Not Intersect(Target, Range("A1:A100")) Is Nothing Then
If Target.Font.ColorIndex <> -4105 Or IsNull(Target.Font.ColorIndex) = True Then
newvalue = Target.Value
Application.Undo
oldvalue = Target.Value
Content = InStr(newvalue, Replace(newvalue, oldvalue, ""))
Target.Value = newvalue
With Target.Characters(Start:=Content, Length:=Len(newvalue)).Font
.Color = 5
End With
Else
Target.Font.ColorIndex = 3
End If
End If
Application.EnableEvents = True
End Sub

Excel Macro Graph Removing Blank Legend Keys

Option Explicit
Public PlotName As String
Public PlotRange As Range
Sub Tester()
Range("TCKWH.V.1").Select
AddPlot ActiveSheet.Range("KWH_G_1")
End Sub
Sub AddPlot(rng As Range)
With ActiveSheet.Shapes.AddChart
PlotName = .Name
.Chart.ChartType = xlLineMarkers
.Chart.SetSourceData Source:=Range(rng.Address())
.Chart.HasTitle = True
.Chart.ChartTitle.Text = Range("KWH.G.1")
.Chart.Axes(xlValue, xlPrimary).HasTitle = True
.Chart.Axes(xlValue, xlPrimary).AxisTitle.Characters.Text = Range("KWH.G.1")
End With
Set PlotRange = rng
Application.EnableEvents = False
rng.Select
Application.EnableEvents = True
End Sub
Sub FixPlott(rng As Range)
Dim n As Long
With ActiveSheet.Shapes(PlotName)
For n = .SeriesCollection.Count To 1 Step -1
With .SeriesCollection(n)
If PlotName = "" Then
.Delete
End If
End With
Next n
End With
End Sub
Sub RemovePlot(rng As Range)
If Not PlotRange Is Nothing Then
If Application.Intersect(rng, PlotRange) Is Nothing Then
On Error Resume Next
rng.Parent.Shapes(PlotName).Delete
On Error GoTo 0
End If
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Application.ScreenUpdating = False
RemovePlot Target
Application.ScreenUpdating = True
End Sub
I need help with Sub FixPlott. I am trying to get it to delete the Legend Entries on the Legend Key. For example if I select Main Campus and South Hall there will be blank legend entries for dunblane and greensburg. Id like the legend just to show selected buildings.
Here you have a corrected version of your sub:
Sub FixPlott(PlotName As String)
Dim n As Long
With ActiveSheet.Shapes(PlotName).Chart
For n = .SeriesCollection.Count To 1 Step -1
With .SeriesCollection(n)
If .Name = "" Then
ActiveSheet.Shapes(PlotName).Chart.Legend.LegendEntries(n).Delete
End If
End With
Next n
End With
End Sub
I am not sure about the exact trigger you want to use. So I have included a simple string trigger; if the given SeriesCollection is called like trigger, the legend will be deleted.