Excel VBA dropdownlist per row if value in AColumn - vba

I´ve been searching for a solution to create a dropdownlist in ColumnC (with start from row 2) if there is value in ColumnA same row.
But all I was able to find is how to create one dropdownlist using VBA.
Sub DVraschwab()
Dim myList$, i%
myList = ""
For i = 1 To 7
myList = myList & "ListItem" & i & ","
Next i
myList = Mid(myList, 1, Len(myList) - 1)
With Range("A5").Validation
.Delete
.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=myList
End With
End Sub
Is this possible? And how should I begin?
The dropdownlist should contain "Yes" and "No" and No would be standard.
This is code that execute when I have written anythins in A Col:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Me.Range("A:A")) Is Nothing Then Exit Sub
Application.EnableEvents = False 'to prevent endless loop
On Error GoTo Finalize 'to re-enable the events
For Each columnAcell In Target.Cells
columnAcell.Offset(0, 3) = Mid(columnAcell, 2, 3)
If IsEmpty(columnAcell.Value) Then columnAcell.Offset(0, 4).ClearContents
Next
Application.ScreenUpdating = False
Dim w1 As Worksheet, w2 As Worksheet
Dim c As Range, FR As Variant
Set w1 = Workbooks("Excel VBA Test.xlsm").Worksheets("AP_Input")
Set w2 = Workbooks("Excel VBA Test.xlsm").Worksheets("Datakom")
For Each c In w1.Range("D2", w1.Range("D" & Rows.Count).End(xlUp))
FR = Application.Match(c, w2.Columns("A"), 0)
If IsNumeric(FR) Then c.Offset(, 1).Value = w2.Range("B" & FR).Value
Next c
Call Modul1.DVraschwab
If Target.Column = 1 Then
If Target.Value = vbNullString Then
Target.Offset(, 2).Clear
End If
End If
Finalize:
Application.EnableEvents = True
End Sub
The module I call is the dropdown you helped me with:
Sub DVraschwab()
Dim myList As String, r As Range
myList = "Yes,No"
For Each r In Range("A2", Range("A" & Rows.Count).End(xlUp))
If r.Value <> vbNullString Then
With r.Offset(, 2).Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Formula1:=myList
End With
r.Offset(, 2).Value = Split(myList, ",")(1)
End If
Next r
End Sub

Do you mean like this? You basically just need a loop added to your code to check column A.
Sub DVraschwab()
Dim myList As String, r As Range
myList = "Yes,No"
For Each r In Range("A2", Range("A" & Rows.Count).End(xlUp))
If r.Value <> vbNullString Then
With r.Offset(, 2).Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Formula1:=myList
End With
r.Offset(, 2).Value = Split(myList, ",")(1)
End If
Next r
End Sub
'this in the relevant sheet module
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 1 and target.row>1 Then
If Target.Value = vbNullString Then
Target.Offset(, 2).Clear
End If
End If
End Sub

This code will set the validation and write the default value in each cell.
Sub DVraschwab()
' 10 Jan 2018
Const MyList As String = "Yes,No"
Dim Rl As Long, R As Long
With Worksheets("Duplicates") ' replace with your sheet's name
' change column from "A" if not applicable
Rl = .Cells(.Rows.Count, "A").End(xlUp).Row
For R = 2 To Rl
With .Cells(R, 3).Validation
.Delete
.Add Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=MyList
End With
.Cells(R, 3).Value = Split(MyList, ",")(1)
Next R
End With
End Sub

Related

Excel VBA Exectution of Private Sub even though if-criteria is not fullfilled

I'm using Private Sub Worksheet_Change(ByVal Target As Range) to react to a changes in Range("AV9:AV" & lastrow) in each of this cells is a dropdown list which is defined as follow:
Dim lastrow2 As Long
Dim lastcell As Long
lastrow2 = Tabelle3.Range("A" & Rows.Count).End(xlUp).Offset(8).Row
lastcell = Tabelle3.Range("AH1048576").End(xlUp).Row
For Each Cell In Tabelle3.Range(Tabelle3.Cells(9, 48), Tabelle3.Cells(lastcell, 48))
If Cell = "" Then
Dim MyList(2) As String
MyList(0) = "Relevant"
MyList(1) = "For Discussion"
MyList(2) = "Not Relevant"
With Tabelle3.Range("AV9:AV" & lastrow2).Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, Formula1:=Join(MyList, Application.International(xlListSeparator))
End With
End If
Next
Those lines are incorporated into a macro which fills Tabelle3with data and all necessary functions, such as the dropdown field.
The Private Sub Worksheet_Change(ByVal Target As Range) is defined as follow:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lastrow As Long
lastrow = Tabelle3.Range("A" & Rows.Count).End(xlUp).Offset(8).Row
On Error Resume Next
If Not Intersect(Target, Range("AV9:AV" & lastrow)) Is Nothing And Target.Value = "Relevant" Or Target.Value = "For Discussion" Then
Application.CutCopyMode = False
Cells(Target.Row, "A").Resize(, 57).Copy
Tabelle14.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Tabelle14.Range("A" & Rows.Count).End(xlUp).PasteSpecial xlPasteFormats
Tabelle14.Range("A" & Rows.Count).End(xlUp).PasteSpecial xlPasteColumnWidths
Application.CutCopyMode = False
End If
If Not Intersect(Target, Range("AV9:AV" & lastrow)) Is Nothing And Target.Value <> "" Then
Cells(Target.Row, "A").Resize(, 2).Copy
Tabelle10.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End If
'//Delete all duplicate rows
Set Rng = Tabelle10.UsedRange
Rng.RemoveDuplicates Columns:=Array(1)
End Sub
As you can see the first part of the Private Sub Worksheet_Change(ByVal Target As Range) 'should' only be executed If in a dropdown field in Range("AV9:AV" & lastrow) the option 'Relevant' or 'For Discussion' is selected and the second part If anything is selceted , therefore I have used Target.Value <> "". This is principally working fine but one bug occurs.
If I insert the data to Tabelle3 through the already mentioned macro, it seems the Private Sub Worksheet_Change(ByVal Target As Range) is then automatically executed for row 9 in Tabelle3and I can find its data in Tabelle14 and Tabelle10 as defined.
Does someone know what's going on here?
Try making these changes:
Option Explicit
Public Sub SetTabelle3Validation()
Const V_LIST = "Relevant,For Discussion,Not Relevant"
Dim ws As Worksheet: Set ws = Tabelle3
Dim lr As Long: lr = ws.Range("AV" & ws.Rows.Count).End(xlUp).Row
Dim app As Application: Set app = Application
Dim fc As Range
If lr > 9 Then
Set fc = ws.Range(ws.Cells(9, "AV"), ws.Cells(lr, "AV"))
fc.Validation.Delete
fc.AutoFilter Field:=1, Criteria1:="<>"
If fc.SpecialCells(xlCellTypeVisible).Cells.Count > 1 Then
app.EnableEvents = False
app.ScreenUpdating = False
With fc.SpecialCells(xlCellTypeVisible).Validation
.Add Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=Join(Split(V_LIST, ","), app.International(xlListSeparator))
End With
app.ScreenUpdating = True
app.EnableEvents = True
End If
fc.AutoFilter
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lr As Long: lr = Me.Rows.Count
Dim lrT3 As Long: lrT3 = Me.Range("A" & lr).End(xlUp).Offset(8).Row
Dim app As Application: Set app = Application
Dim inAV As Boolean
inAV = Not Intersect(Target, Me.Range("AV9:AV" & lrT3)) Is Nothing
With Target
If .Cells.CountLarge > 1 Or Not inAV Or Len(.Value) = 0 Then Exit Sub
app.EnableEvents = False
If .Value = "Relevant" Or .Value = "For Discussion" Then
Me.Cells(.Row, "A").Resize(, 57).Copy
With Tabelle14.Range("A" & lr).End(xlUp).Offset(1)
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
.PasteSpecial xlPasteColumnWidths
End With
Tabelle14.UsedRange.RemoveDuplicates Columns:=Array(1)
End If
Me.Cells(.Row, "A").Resize(, 2).Copy
With Tabelle10
.Range("A" & lr).End(xlUp).Offset(1).PasteSpecial xlPasteValues
.UsedRange.RemoveDuplicates Columns:=Array(1)
End With
app.CutCopyMode = False
app.EnableEvents = True
End With
End Sub
In SetTabelle3Validation()
Replace For loop with AutoFilter for speed
Turn Application.EnableEvents Off to stop triggering Worksheet_Change() (then back On)
In Worksheet_Change()
Exit the Sub if pasting multiples values, Target is not in col AV, or is empty
Else (Target is in col AV, and not empty)
Turn Application.EnableEvents Off
If Target value is "Relevant" Or "For Discussion", update Tabelle14
Else (Target value is "Not Relevant"), update Tabelle10
Turn Application.EnableEvents On
Assumptions
All objects starting with Tabelle are the Code Names of other sheets
Worksheet_Change() belongs to Tabelle3

How to alter code with FOR Each Loop to to avoid Error: 1004 No cells found

I have a macro that looks below header names for items if there is an item it will make it a drop down. Headers are in the 7th row so it starts looking from row 8 and on. The code runs perfectly, except if there is no items below the headers.
Sometimes the user does not need any drop downs for the sheet so they will leave all rows below the headers blank. Which is great for what I am doing but will make the macro throw errors as there is no items to be found.
I essentially need to tweak my code so it is able to stop or exit if no cells are found. This is the macro I need to tweak.
Sub AddDropDowns()
Dim cell As Range
Dim iDropDown As Long
With Worksheets("Sheet1")
For Each cell In .Range("B8", .Cells(8, .Columns.Count).End(xlToRight)).SpecialCells(XlCellType.xlCellTypeConstants)
AddDropDown Worksheets("DropDownsTT"), iDropDown, cell.Offset(-1).Value, "='" & .Name & "'!" & cell.Resize(WorksheetFunction.CountA(cell.EntireColumn) - 1).Address
Next cell
End With
End Sub
Not sure if this piece of code is needed but the macro calls the following subroutine:
Sub AddDropDown(sht As Worksheet, dropDownCounter As Long, header As String, validationFormula As String)
With sht.Range("A1").Offset(, dropDownCounter) '<--| reference passed sheet row 1 passed column
.Cells(1, 1) = header '<--| write header
With .Cells(2, 1).Validation '<--| reference 'Validation' property of cell 1 row below currently referenced one
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, Formula1:=validationFormula
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = True
End With
End With
dropDownCounter = dropDownCounter + 1
End Sub
You could do this:
Dim rng As Range
'...
With Worksheets("Sheet1")
On Error Resume Next
Set rng = .Range("B8", .Cells(8, .Columns.Count).End( _
xlToRight)).SpecialCells(XlCellType.xlCellTypeConstants)
On Error Goto 0
If Not rng Is Nothing Then
For Each cell In rng
AddDropDown Worksheets("DropDownsTT"), iDropDown, _
cell.Offset(-1).Value, "='" & .Name & "'!" & _
cell.Resize(WorksheetFunction.CountA(cell.EntireColumn) - 1).Address
Next cell
End If
End With
but that's kind of untidy, so I would probably use something like:
With Worksheets("Sheet1")
For Each cell In .Range("B8", .Cells(8, .Columns.Count).End( xlToRight))
If Len(cell.Value) > 0 Then
AddDropDown Worksheets("DropDownsTT"), iDropDown, _
cell.Offset(-1).Value, "='" & .Name & "'!" & _
cell.Resize(WorksheetFunction.CountA(cell.EntireColumn) - 1).Address
End If
Next cell
End With

Form Data to Particular Cells

In Excel sheet2 i have Columns A & D for Name, B & E Start Date and column C & F is End Date and a Form with ComboBox (loaded with names) and two Textboxes.
I want when I click submit button it will search the columns for a name that matches the ComboBox value and then write the values of the two TextBoxes into the right adjacent two EMPTY cells
Private Sub CommandButton4_Click()
Dim irow As Long
Dim ws As Worksheet
Set ws = Worksheets("Sheet2")
With ws
.Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Value = Me.Combo.Value
.Cells(.Rows.Count, "B").End(xlUp).Offset(1, 0).Value = Me.sttdate.value
.Cells(Rows.Count, "C").End(xlUp).Offset(1, 0).Value = Me.enddate.Value
End With
With Me
.Combo.Value = ""
.startdate.Value = ""
.enddate.Value = ""
End With
End Sub
This code is adding value of all form into Columns A B & C
This should do the trick. I added some checks based on what you wrote in your explanation in case it helps.
Private Sub CommandButton4_Click()
Dim irow As Long
Dim ws As Worksheet
Set ws = Worksheets("Sheet2")
With ws
irow = .Range("A" & .Rows.Count).End(xlup).Row
Dim rFound as Range
Set rFound = .Range("A1:A" & iRow).Find(Me.Combo.Value, lookat:=xlWhole)
If not rFound is Nothing Then
If IsEmpty(rFound.Offset(,1)) and IsEmtpy(rFound.Offset(,2)) Then
rFound.Offset(,1) = Me.sttdate.value
rFound.Offset(,2) = Me.enddate.value
With Me
.Combo.Value = ""
.startdate.Value = ""
.enddate.Value = ""
End With
Else
Msgbox "Name already has values"
End If
Else
Msgbox "Name not Found"
End If
End Sub
This should work just fine :
Private Sub CommandButton4_Click()
Dim irow As Long, _
wS As Worksheet, _
NextRow As Long, _
cF As Range
Set wS = Worksheets("Sheet2")
With wS
With .Range("A:A")
'First, define properly the Find method
Set cF = .Find(What:=Me.Combo.Value, _
After:=.Cells(1, 1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
End With
'If there is a result, keep looking with FindNext method
If Not cF Is Nothing Then
If cF.Offset(0, 1) <> vbNullString Then
Set cF = cF.End(xlToRight).Offset(0, 1)
cF.Value = Me.sttdate.Value
cF.Offset(0, 1).Value = Me.EndDate.Value
Else
.Cells(cF.Row, "B").Value = Me.sttdate.Value
.Cells(cF.Row, "C").Value = Me.EndDate.Value
End If
Else
NextRow = .Cells(.Rows.Count, "A").End(xlUp).Offset(1, 0).Row
.Cells(NextRow, "A").Value = Me.Combo.Value
.Cells(NextRow, "B").Value = Me.sttdate.Value
.Cells(NextRow, "C").Value = Me.EndDate.Value
End If
End With
With Me
.Combo.Value = ""
.StartDate.Value = ""
.EndDate.Value = ""
End With
End Sub

Run-time Error '9'. Sub-script out of range error in Excel VBA

I'm getting a
Run-time Error '9' : Sub-script out of range.
Option Explicit
Sub DistributeRows()
Dim a As Variant, h As String
Dim i As Long, nr As Long
Dim rng As Range, c As Range, v
Application.ScreenUpdating = False
With Sheets("Sheet1")
a = .Cells(1).CurrentRegion
Set rng = .Range("M2:M" & UBound(a, 1))
End With
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
For Each c In rng
If c <> "" Then
If Not .Exists(c.Value) Then
.Add c.Value, c.Value
End If
End If
Next
v = Application.Transpose(Array(.keys))
End With
For i = LBound(v) To UBound(v)
h = v(i, 1)
If Not WorksheetExists(h) Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = h
Sheets(h).Range("A1").Resize(, UBound(a, 2)).Value = Sheets("Sheet1").Range("A1").Resize(, UBound(a, 2)).Value
End If
Next i
For i = 2 To UBound(a, 1)
h = a(i, 3)
nr = Sheets(h).Range("A" & Rows.Count).End(xlUp).Offset(1).Row
Sheets(h).Range("A" & nr).Resize(, 3).Value = Sheets("Sheet1").Cells(i, 1).Resize(, 3).Value
Sheets(h).Columns.AutoFit
Next i
Sheets("Sheet1").Activate
Application.ScreenUpdating = True
End Sub
Function WorksheetExists(WSName As String) As Boolean
On Error Resume Next
WorksheetExists = Worksheets(WSName).Name = WSName
On Error GoTo 0
End Function
I'm getting the error on this line.
nr = Sheets(h).Range("A" & Rows.Count).End(xlUp).Offset(1).Row
The excel sheet I'm trying to pull it from has information like this
Example.
Dropbox file related to the error
https://dl.dropboxusercontent.com/u/64819855/StackOverflow.xlsx
The goal of this script is to create new tabs in the worksheet based on the "Current Location (Column M)". I have multiple current locations (maybe 100+).
Then it would copy all the data relating to Column M. E.g. everything in Los Angeles, would be copied to the Los Angeles Tab.
Thanks.
I modified the code and understood what the problem was. Here's the updated code and if you guys ever need to do something similar - hope this helps.
Option Explicit
Sub DistributeRows()
Dim a As Variant, h As String
Dim i As Long, nr As Long
Dim rng As Range, c As Range, v
Application.ScreenUpdating = False
//Change Range("XX#:X" to whatever you want to create new tabs from.
With Sheets("Sheet1")
a = .Cells(1).CurrentRegion
Set rng = .Range("M2:M" & UBound(a, 1))
End With
With CreateObject("Scripting.Dictionary")
.CompareMode = vbTextCompare
For Each c In rng
If c <> "" Then
If Not .Exists(c.Value) Then
.Add c.Value, c.Value
End If
End If
Next
v = Application.Transpose(Array(.keys))
End With
For i = LBound(v) To UBound(v)
h = v(i, 1)
If Not WorksheetExists(h) Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = h
Sheets(h).Range("A1").Resize(, UBound(a, 2)).Value = Sheets("Sheet1").Range("A1").Resize(, UBound(a, 2)).Value
End If
Next i
For i = 2 To UBound(a, 1)
h = a(i, 13)
nr = Sheets(h).Range("A" & Rows.Count).End(xlUp).Offset(1).Row
Sheets(h).Range("A" & nr).Resize(, 16).Value = Sheets("Sheet1").Cells(i, 1).Resize(, 16).Value
Sheets(h).Columns.AutoFit
Next i
// Change the Resize(, XX) to whatever you want to copy until.
// Also change the H = a(i,XX) to whatever column your "tab names" are at.
//
Sheets("Sheet1").Activate
Application.ScreenUpdating = True
End Sub
Function WorksheetExists(WSName As String) As Boolean
On Error Resume Next
WorksheetExists = Worksheets(WSName).Name = WSName
On Error GoTo 0
End Function

Copy excel row in different worksheet when cell dropdown "Yes" and when "No" removes the row if "Yes" was selected previously

I am trying to copy excel row in different worksheet sheet 2 when cell dropdown "Yes" of Column F and when "No" removes the row if "Yes" was selected previously. I also wanted to check if duplicate exists in worksheet 2, then prompt user with "Yes", "No" button. If "Yes" then duplicate if "No" do nothing.
ColA:Customer Name ColB:Customer Address ColC:Customer City ColD:Cust zip ColE:Tel ColF:Yes/No
I have tried this.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim lastrow As Long
If UCase(Range("F" & ActiveCell.Row).Value) <> "YES" Then Exit Sub
With ThisWorkbook.Worksheets("Sheet2")
lastrow = Application.Max(4, .Cells(.Rows.Count, "A").End(xlUp).Row + 1)
If WorksheetFunction.CountIf(.Range("A1:A" & lastrow), _
Range("A" & ActiveCell.Row).Value) > 0 Then Exit Sub
Response = MsgBox("Record already exists, add again?", vbQuestion + vbYesNo + 256)
If Response = vbNo Then Exit Sub
.Range("A" & lastrow).Resize(, 5).Value = _
Range("A" & ActiveCell.Row).Resize(, 5).Value
End With
Response = MsgBox("Record added")
End Sub
If I understand you correctly, you need something like this (code runs only if changed value in column F):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lastrow As Long
Dim Response
Dim rng As Range, rngToDel As Range
Dim fAddr As String
If Intersect(Target, Range("F:F")) Is Nothing Then Exit Sub
Application.EnableEvents = False
On Error GoTo ErrHandler
With ThisWorkbook.Worksheets("Sheet2")
lastrow = Application.Max(4, .Cells(.Rows.Count, "A").End(xlUp).Row + 1)
If UCase(Target.Value) = "YES" Then
Response = vbYes
If WorksheetFunction.CountIf(.Range("A1:A" & lastrow), _
Range("A" & Target.Row).Value) > 0 Then
Response = MsgBox("Record already exists, add again?", vbQuestion + vbYesNo + 256)
End If
If Response = vbYes Then
.Range("A" & lastrow).Resize(, 5).Value = _
Range("A" & Target.Row).Resize(, 5).Value
MsgBox "Record added"
End If
ElseIf UCase(Target.Value) = "NO" Then
With .Range("A4:A" & lastrow)
Set rng = .Find(What:=Range("A" & Target.Row), _
LookIn:=xlValues, _
lookAt:=xlWhole, _
MatchCase:=False)
If Not rng Is Nothing Then
fAddr = rng.Address
Do
If rngToDel Is Nothing Then
Set rngToDel = rng.Resize(, 5)
Else
Set rngToDel = Union(rngToDel, rng.Resize(, 5))
End If
Set rng = .FindNext(rng)
If rng Is Nothing Then Exit Do
Loop While fAddr <> rng.Address
End If
If Not rngToDel Is Nothing Then
rngToDel.Delete Shift:=xlUp
MsgBox "Records from sheet2 removed"
End If
End With
End If
End With
ExitHere:
Application.EnableEvents = True
Exit Sub
ErrHandler:
Resume ExitHere
End Sub