Add text value to adjacent target range via Vlookup macro - vba

Good afternoon, I would like by means of the changed cell value macro function
in Sheet1.Range ("I15:I18") to introduce a text value based on Vlookup function, avoiding using the formula. This is the table that Vlookup function text is looking at:
A B
1 0 Low Risk
2 10 Medium Risk
3 15 High Risk
It follows the code that it doesn't work for me:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Dim num As Long
Dim sRes As Variant
Set KeyCells = Sheet1.Range("I15:I18")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
sRes = Application.VLookup(num, Sheet2.Range("A56:B58"), 2, True)
Debug.Print sRes
Sheet1.Target.Offset(0, 1).Text = sRes
End If
End Sub
The actual score that falls in the range is triggered by another macro that it works perfectly.
Here also follow the macro that works alright with a single cell:
Sub NumberVLookup()
Dim num As Long
num = 16
Dim sRes As Variant
sRes = Application.VLookup(num, Sheet2.Range("A56:B58"), 2, True)
Debug.Print sRes
Sheet2.Range("J15") = sRes
End Sub
I really appreciate your help in this regard.

Untested:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Dim sRes As Variant
on error goto haveError
Set rng = Application.Intersect(Me.Range("I15:I18"), Target)
If Not rng Is Nothing Then
If rng.cells.count = 1 then
sRes = Application.VLookup(rng.Value, _
Sheet2.Range("A56:B58"), 2, True)
'turn off events before updating the worksheet
Application.enableEvents = False
rng.Offset(0, 1).Value = IIf(IsError(sRes), "???", sRes)
Select Case rng.Offset(0, 1).Value
Case "Low Risk": rng.Offset(0, 2).Value = Date + 180
Case "Medium Risk": rng.Offset(0, 2).Value = Date + 150
Case "High Risk": rng.Offset(0, 2).Value = Date + 120
End Select
Application.enableEvents = True
End If '<< edit added missing line here
End If
Exit Sub
haveError:
Application.enableEvents = True '<< ensures events are reset
End Sub

Related

Have more than one Worksheet_Change in a Worksheet

I am looking to limit my workbook users to 1000 characters over a range of cells (Example: A5:A30).
In other words limit the total characters in the range A5:A30 to 1000 characters.
When a user fills in a cell that sends the range over the 1000 character limit, it will call Application.undo which should just remove the last text that they added.
However since I have another Private Sub Worksheet_Change(ByVal Targe As Range) on the worksheet, it causes a bug.
Below is both Worksheet_Change subs. Both use the same cells.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim charCount As Long
If Not Intersect(Target, Range("E6,E11,E16")) Is Nothing Then
Dim arrValues As Variant
arrValues = Range("E6,E11,E16").Value2
Dim i As Long
Dim tempSplit As Variant
Dim j As Long
For i = LBound(arrValues) To UBound(arrValues)
tempSplit = Split(arrValues(i, 1), " ")
For j = LBound(tempSplit) To UBound(tempSplit)
charCount = charCount + Len(tempSplit(j))
Next j
Next i
End If
If charCount > 1000 Then
Application.Undo
MsgBox "Adding this exceeds the 1000 character limit"
End If
If Not Intersect(Target, Range("D6")) Is Nothing Then
If Target.Value2 = "Material" Then
'assumes the comment cell is one column to the right
Target.Offset(0, 1) = "**"
End If
End If
If Not Intersect(Target, Range("D7")) Is Nothing Then
If Target.Value2 = "Material" Then
'assumes the comment cell is one column to the right
Target.Offset(-1, 1) = "**"
End If
End If
If Not Intersect(Target, Range("D8")) Is Nothing Then
If Target.Value2 = "Material" Then
Target.Offset(-2, 1) = "**"
End If
End If
End Sub
Is there a way around this so I can have two Worksheet_Change on the same worksheet?
You cannot have two Worksheeet_Change events in one sheet. But, one is quite enough:
Private Sub Worksheet_Change(ByVal Target As Range)
Select Case True
Case Not Intersect(ActiveCell, Range("A5:A30")) Is Nothing
DoThingOne
Case Not Intersect(ActiveCell, Range("B5:B30")) Is Nothing
DoThingTwo
End Select
End Sub
Private Sub DoThingOne()
Debug.Print "THING ONE"
End Sub
Private Sub DoThingTwo()
Debug.Print "THING TWO"
End Sub
How about this revision using Vityata's idea?
Private Sub Worksheet_Change(ByVal Target As Range)
Select Case True
Case Not Intersect(Target, Range("E6,E11,E16")) Is Nothing
Dim charCount As Long
Dim arrValues As Variant
arrValues = Range("E6,E11,E16").Value2
Dim i As Long
Dim tempSplit As Variant
Dim j As Long
For i = LBound(arrValues) To UBound(arrValues)
tempSplit = Split(arrValues(i, 1), " ")
For j = LBound(tempSplit) To UBound(tempSplit)
charCount = charCount + Len(tempSplit(j))
Next j
Next i
If charCount > 1000 Then
With Application
.EnableEvents = False
.Undo
.EnableEvents = True
End With
MsgBox "Adding this exceeds the 1000 character limit"
End If
Case Not Intersect(Target, Range("D6")) Is Nothing
If Target.Value2 = "Material" Then
'assumes the comment cell is one column to the right
Target.Offset(0, 1) = "**"
End If
Case Not Intersect(Target, Range("D7")) Is Nothing
If Target.Value2 = "Material" Then
'assumes the comment cell is one column to the right
Target.Offset(-1, 1) = "**"
End If
Case Not Intersect(Target, Range("D8")) Is Nothing
If Target.Value2 = "Material" Then
Target.Offset(-2, 1) = "**"
End If
End Select
End Sub

Excel VBA Large Table, Add Comments Vlookup, After Hitting Command Button

I have a large table and the information I'm wanting to add comments to falls within Range(D11:CY148). I have two tabs - "Finish Matrix" (main) and "list" (hidden - has 2 columns).
I have two issues.
First issue - Code works to a degree, after I type my values within a cell it automatically adds comments based off info in another sheet. The problem is there is too many cells to be manually typing into and if I copy and paste the code doesn't run. I created a CommandButton and wanted it to refresh the entire table with comments depending if the cells had the values that fall within "list". I tried to create a call out to Worksheet_Change but to no avail. (I'm a beginner so it'll help if you explain)
Second issue - I'm assuming it'll get fixed with whatever suggestion that works. Occasionally after typing into a cell I would get an error. Can't remember the error name but it is one of the common ones, atm the error isn't popping up but surely it'll come back since I didn't do anything different to the code.
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Columns("A:CX")) Is Nothing Then _
If Intersect(Target, Columns("CY")) Is Nothing Then Exit Sub
Dim lRow As Integer
lRow = Sheets("list").Range("A1").End(xlDown).Row
If Target.Value = vbNullString Then Target.ClearComments
For Each cell In Sheets("list").Range("A1:A" & lRow)
If cell.Value = Target.Value Then
Target.AddComment
Target.Comment.Text Text:=cell.Offset(0, 1).Value
End If
Next cell
End Sub
Thanks for any and all help!
You are basically missing the For Each Cell in Target part...
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim wsMain As Worksheet, wsList As Worksheet
Dim cell As Range
Dim vCommentList As Variant
Dim i As Long, lLastRow As Long
Dim sValue As String
On Error GoTo ErrHandler
Application.ScreenUpdating = False
Application.EnableEvents = False
Set wsMain = Target.Parent
Set Target = Intersect(Target, wsMain.Range("D11:CY148"))
If Target Is Nothing Then Exit Sub
Set wsList = wsMain.Parent.Sheets("list")
lLastRow = LastRow(1, wsList)
' Read Comment List into Variant (for speed)
vCommentList = wsList.Range("A1:B" & lLastRow)
Target.ClearComments
' This...For each Cell in Target...is what you were missing.
For Each cell In Target
sValue = cell
For i = 1 To UBound(vCommentList)
If sValue = vCommentList(i, 1) Then
AddComment cell, CStr(vCommentList(i, 2))
Exit For
End If
Next
Next
ErrHandler:
If Err.Number <> 0 Then Debug.Print Err.Description
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Proper way to find last row ...
Public Function LastRow(Optional Col As Integer = 1, Optional Sheet As Excel.Worksheet) As Long
If Sheet Is Nothing Then Set Sheet = Application.ActiveSheet
LastRow = Sheet.Cells(Sheet.Rows.Count, Col).End(xlUp).Row
End Function
Add Comment Sub the allows appending is needed...
Public Sub AddComment(Target As Range, Text As String)
If Target.Count = 1 Then
If Target.Comment Is Nothing Then
Target.AddComment Text
Else
Target.Comment.Text Target.Comment.Text & vbLf & Text
End If
End If
End Sub
Untested, but this will take all the values in Range(D11:CY148) and add a comment based on a lookup from Sheet "list".
Sub testy()
Dim arr As Variant, element As Variant
Dim i As Long, j As Long, listItems As Long, rwLast As Long, clLast As Long
Dim comm As String
Dim rng As Range, cell As Range
listItems = Sheets("list").Range("A1").End(xlDown).Row
rwLast = Cells.SpecialCells(xlCellTypeLastCell).Row ' Adjust to fit your needs
clLast = Cells.SpecialCells(xlCellTypeLastCell).Column 'Idem
Set rng = Sheets("list").Range("A1:A" & listItems)
arr = Range("D11:CY148").Value
With Worksheets("Finish Matrix")
For i = 1 To rwLast - 10 'Adjust to make it more general, this is pretty rough
For j = 1 To clLast - 3 'Idem
If i = 3 Then
End If
comm = ""
For Each cell In rng
If arr(i, j) = cell.Value Then
comm = comm & Chr(13) & cell.Offset(0, 1).Value
End If
Next cell
If Not (comm = "") Then
.Cells(10, 3).Offset(i, j).ClearComments
.Cells(10, 3).Offset(i, j).AddComment
.Cells(10, 3).Offset(i, j).Comment.Text Text:=comm
End If
Next j
Next i
End With
End Sub

Automatic re-numbering of prioritized list VBA

I have a list of priorities in Excel, for example:
4,
1,
3,
2
I want it to automatically update the priorities, if I enter a new name and assigns a priority. For example, if the new name has a priority of 2.
5,
1,
4,
3,
2
I have the following VBA code, which should do the job, but I can't get it to run.
Sub Worksheet_Change(ByVal Target As Range)
Dim rngPriorityList As Range
Dim lNewValue As Integer
Dim myCell As Range
If IsNumeric(Target.Value) Then 'Only run if the a number was entered
Set rngPriorityList = Intersect(Target, Range("I3:I500")) 'the named range for the task list
If Not Intersect(Target, rngPriorityList) Is Nothing Then 'Only run the following in the cell being updated was in the priority list range
If Target.Value >= 1 Then
For Each myCell In rngPriorityList.Cells 'Loop through the priority list range
If myCell.Value = Target.Value _
And myCell.Address <> Target.Address Then 'Finding cells with the same value, excluding the cell being changes
myCell.Value = myCell.Value + 1 'Increment the prioriry by 1
End If
Next myCell
End If
End If
End If
End Sub
a possible correction
wrong setting of rngprioritylist (should be Range("I3:I500" i.o. intersect( ...))
wrong test of mycell.value (should be >= i.o. =)
events should be disabled during the adaptation of the priorities as modifying the priorities will trigger the worksheet_change event.
Sub Worksheet_Change(ByVal Target As Range)
Dim rngPriorityList As Range
Dim lNewValue As Integer
Dim myCell As Range
If IsNumeric(Target.Value) Then 'Only run if the a number was entered
Set rngPriorityList = Range("I3:I500") 'the named range for the task list
If Not Intersect(Target, rngPriorityList) Is Nothing Then 'Only run the following in the cell being updated was in the priority list range
If Target.Value >= 1 Then
Application.EnableEvents = False
For Each myCell In rngPriorityList.Cells 'Loop through the priority list range
If myCell.Value >= Target.Value _
And myCell.Address <> Target.Address Then 'Finding cells with the same value, excluding the cell being changes
myCell.Value = myCell.Value + 1 'Increment the prioriry by 1
End If
Next myCell
Application.EnableEvents = True
End If
End If
End If
End Sub

Two loop in one code

I could use some assistance correcting the code below as what show when activated is the first image while I want to do the second image.
Also if you have other code to do the same job, please do. thanks in advance for your assistance.
Private Sub Worksheet_Activate()
Dim rng As Range, cell As Range
Dim a As Range, az As Range
Application.EnableEvents = False
Set rng = Range("A2:AE2")
Set az = Range("A3:AE6")
For Each cell In rng
For Each a In az
If cell.Value = "Fri" Then
a.Value = "Fri"
ElseIf cell.Value = "Sat" Then
a.Value = "Sat"
End If
Next a
Next cell
Application.EnableEvents = True
End Sub
Use the { and the } in the styling/headers section, above where you type, to insert formatted code next time please so that it looks like this. :)
Edited with your answer:
Private Sub Worksheet_Activate()
Dim rng As Range, cell As Range
Dim a As Range, az As Long 'set az = number of rows you want filled with fri/sat
Application.EnableEvents = False
Set rng = Range("A2:AE2")
az = 4
For Each cell In rng
If cell.Value = "fri" Then
For i = 1 To az
cell.Offset(i).Value = "fri"
Next i
ElseIf cell.Value = "sat" Then
For i = 1 To az
cell.Offset(i).Value = "sat"
Next i
End If
Next cell
Application.EnableEvents = True
End Sub
You get the result because you do it for each cell in az, but you dont wan't to do it so, you have to fill just the column of the found Fri or Sat.
Private Sub Worksheet_Activate()
Dim rng As Range, cell As Range
Application.EnableEvents = False
Set rng = Range("B2:BE2")
For Each cell In rng
If cell.value = "Fri" Then
For i as Integer = 3 To 6 Step 1
Cells(i,cell.column).Value = "Fri"
Next
End If
If cells.value = "Sat" Then
For i as Integer = 3 To 6 Step 1
Cells(i,cell.column).Value = "Sat"
Next
End If
Next cell
Application.EnableEvents = True
End Sub
It should be something like that i think
Also if you have other code to do the same job, please do.
The following will ask to build a new calendar worksheet based upon the current month every time you create a new worksheet.
        ThisWorkbook code sheet:
Option Explicit
Private Sub Workbook_NewSheet(ByVal Sh As Object)
If MsgBox("Create new calendar?", vbYesNo, "AutoBuild") <> vbYes Then Exit Sub
'the following DELETES ANY WORKSHEET WITH THE SAME MONTH/YEAR NAME
On Error Resume Next
Application.DisplayAlerts = False
Worksheets(Format(Date, "mmm yyyy")).Delete
Application.DisplayAlerts = True
On Error GoTo 0
'create a new calendar worksheet based on the current month
With Sh
Dim c As Long
.Name = Format(Date, "mmm yyyy")
With .Cells(1, 1).Resize(6, Day(DateSerial(Year(Date), Month(Date) + 1, 0)))
.Formula = "=DATE(" & Year(Date) & ", " & Month(Date) & ", COLUMN())"
.Value = .Value
.Rows(1).NumberFormat = "d"
.Rows(2).Resize(.Rows.Count - 1, .Columns.Count).NumberFormat = "ddd"
.EntireColumn.ColumnWidth = 5 'AutoFit
.HorizontalAlignment = xlCenter
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
With .FormatConditions
.Delete
.Add Type:=xlExpression, Formula1:="=AND(ROW()>2, WEEKDAY(A2, 15)>2)"
.Add Type:=xlExpression, Formula1:="=WEEKDAY(A2, 15)<3"
.Add Type:=xlExpression, Formula1:="=AND(ROW()=2, WEEKDAY(A2, 15)>2)"
End With
.FormatConditions(1).NumberFormat = ";;;"
.FormatConditions(2).Interior.Color = 5287936
.FormatConditions(3).Interior.Color = 14281213
End With
End With
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
.FreezePanes = True
.Zoom = 80
End With
End With
End Sub
You will likely want to make adjustments but this may be a good framework to get started. I've taken the approach of using the actual dates and displying their day-of-the-month and day-of-the-week through cell Number Format Codes. This leaves the raw underlying date value(s) available for calculation and lookup. Similarly, the dates that appear blank are not actually blank; the custom number format that has been applied through Conditional Formatting simply shows no value at all in the cell.
  
I've found an answer to part of the question, but I need help to complete the code as it applies to one row only.
Private Sub Worksheet_Activate()
Dim cell As Range, rng As Range
Application.EnableEvents = False
Set rng = Range("A2:AE2")
For Each cell In rng
If Cells(2, cell.Column) = "Fri" Then
Cells(3, cell.Column) = "Fri"
ElseIf Cells(2, cell.Column) = "Sat" Then
Cells(3, cell.Column) = "Sat"
End If
Next cell
Application.EnableEvents = True
End Sub

Copy a row of a table when a cell in a specified column changes

I'm trying to copy the row in a table when a cell in a specified column has data inserted then paste this row into another sheet.
The table starts at cell A3 being the first header to the table and it is 9 columns long, there will be an endless amount of rows.
The column to monitor for change is column 8, named "Date Complete". The information entered should always be a date, format "dd mmm".
The row needs to be copied onto a sheet with the same name as the date entered into column 8 which may not exist before the date is entered.
Also before the copying is done I would like a text box to enter notes into the corresponding cell in column 9, named "Notes".
Private Sub Worksheet_change(ByVal Target As Range)
Const lngdatecomplete As Long = 8
Dim wks As Worksheet
Dim lngNextAvailableRow As Long
If Target.Areas.Count = 1 And Target.Cells.Count = 1 Then
If Not Intersect(Target, Columns(lngdatecomplete)) Is Nothing Then
On Error Resume Next
Set wks = ThisWorkbook.Worksheets(Target.Value)
On Error GoTo 0
If wks Is Nothing Then
lngNextAvailableRow = wks.Range("a1").CurrentRegion.Rows.Count + 1
ActiveSheet.Range(Cells(Target.Row, 2), Cells(Target.Row, 8)).copy _
wks.Range("A" & lngNextAvailableRow).PasteSpecial
ElseIf Not wks Is Nothing Then
Dim ShtName$
Sheets.Add after:=Sheets(Sheets.Count)
ShtName = Format(Date, "dd mmm")
Sheets(Sheets.Count).Name = ShtName
Sheets(ShtName).Visible = True
lngNextAvailableRow = wks.Range("a1").CurrentRegion.Rows.Count + 1
ActiveSheet.Range(Cells(Target.Row, 2), Cells(Target.Row, 8)).copy _
wks.Range("A" & lngNextAvailableRow).PasteSpecial
End If
End If
End If
End Sub
The following seems pretty robust and will accept multiple values pasted into column H. I would advise setting a breakpoint on the Application.EnableEvents = False code line and typing a date into column H. Once you arrive at the breakpoint, you can step through each line with the F8 key.
Private Sub Worksheet_change(ByVal Target As Range)
Const lDATECMPLT As Long = 8
If Not Intersect(Target, Columns(lDATECMPLT)) Is Nothing Then
On Error GoTo bm_Safe_Exit
'Application.ScreenUpdating = False
Application.EnableEvents = False
Dim trgt As Range
For Each trgt In Intersect(Target, Columns(lDATECMPLT))
If trgt.Row > 3 And IsDate(trgt) Then
trgt.NumberFormat = "dd mmm"
On Error GoTo bm_Need_WS
With Worksheets(trgt.Text)
On Error GoTo bm_Safe_Exit
trgt.Resize(1, 7).Offset(0, -6).Copy _
Destination:=.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
'optional mark the row copied
'With trgt.Resize(1, 7).Offset(0, -6).Font
' .Strikethrough = True
' .Color = RGB(120, 120, 120)
'End With
End With
End If
Next trgt
End If
GoTo bm_Safe_Exit
bm_Need_WS:
On Error GoTo 0
With Worksheets.Add(after:=Sheets(Sheets.Count))
.Name = trgt.Text
.Visible = True
.Cells(1, 1).Resize(1, 7) = Me.Cells(3, 2).Resize(1, 7).Value2
With ActiveWindow
.SplitColumn = 0
.SplitRow = 1
.FreezePanes = True
.Zoom = 80
End With
End With
Resume
bm_Safe_Exit:
Application.EnableEvents = True
Me.Activate
Application.ScreenUpdating = True
End Sub
I left some extras like copying the headers from the original worksheet into the new worksheet, freezing row 1 on the new worksheet, zooming the new worksheet, etc. Delete or adjust these these if you do not find them helpful.
When you have made all adjustments to the code, uncomment the 'Application.ScreenUpdating = False code line to avoid screen flashes.