excel dropdown with address as result - excel

A normal data validation dropdown in Excel results in the selected value being put into the cell. In my case though, I am referencing another list in my sheet whose elements can change. My goal is to make those changes apply to already selected dropdown items.
Example:
Referenced list in dropdown (sheet "List"):
A
B
C
User selects A from the dropdown in sheet "Selection":
A
Now the user changes A to Y in sheet "List":
Y
B
C
The user's selection in sheet "Selection" still shows A, but it should now show Y:
A
Is this possible in any way? Can I e.g. make the dropdown result in an address to the value, instead of the value itself?
Thanks!

There unfortunately isn't any way to do this with a formula or build-in function (that I'm aware of)
Here is something simple you could apply and work with:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target(1, 1), Range("A1:A3")) Is Nothing Then
ActiveWorkbook.Sheets("Selection").Range("A1").Value = Target(1, 1)
End If
End Sub
Assuming Range("A1:A3") is the list you are refering to. Paste this under your List sheet.

Drop Down feat. Worksheet Change Event
To 'copy' your setup, in worksheet List I have created a name
Drop1 which refers to the column range containing the values. Then
I have created a Validation Drop Down in B2 in worksheet
Selection and chose the name (Drop1) as the list.
Change the constants (Const) to fit your needs.
Module1
Option Explicit
Public strListSheet As String
Public strListRange As String
Public vntList As Variant
Sub Drop(rngList As Range)
Const cDropSheet As String = "Selection"
Const cDropRange As String = "B2"
Dim rng As Range
Dim vntNew As Variant
Dim vntVal As Variant
Dim Nor As Long
Dim i As Long
Set rng = ThisWorkbook.Worksheets(cDropSheet).Range(cDropRange)
vntVal = rng
vntNew = rngList
Nor = UBound(vntList)
For i = 1 To Nor
If vntList(i, 1) = vntVal Then
If vntVal <> vntNew(i, 1) Then
rng = vntNew(i, 1)
End If
Exit For
End If
Next
vntList = vntNew
End Sub
Sub Initialize()
Const strDrop as string = "Drop1"
Dim str1 As String
Dim lngInStr As Long
' Prepare
str1 = Names(strDrop).RefersTo
lngInStr = InStr(1, str1, "!")
' Write Public Variables
strListRange = Right(str1, Len(str1) - lngInStr)
strListSheet = WorksheetFunction.Substitute(WorksheetFunction _
.Substitute(Left(str1, lngInStr - 1), "=", ""), "'", "")
vntList = Worksheets(strListSheet).Range(strListRange)
End Sub
ThisWorkbook
Option Explicit
Private Sub Workbook_Open()
Initialize
End Sub
List (Worksheet)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrInit
If Target.Cells.Count = 1 Then
Dim rngList As Range
Set rngList = ThisWorkbook.Worksheets(strListSheet) _
.Range(strListRange)
If Not Intersect(Target, rngList) Is Nothing Then
Drop rngList
End If
End If
Exit Sub
ErrInit:
MsgBox "An unexpected error occurred. Error '" & Err.Number & "':" _
& Err.Description, vbCritical, "Error"
On Error GoTo 0
Initialize
End Sub

Related

Get address of cell that triggered userform load, populate input fields via offset

I load a userform via a double-click event executed on a range of cells. Once any of the cells in the range gets double clicked, my userform is loaded.
I would like the input boxes of the userform populated with data that is based on an offset of the triggering cell.
I am struggling with capturing the address of the cell that triggered the event, and consequently would need to figure out how to offset from that cell's column and obtain the relevant value for population in the userform.
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Application.Intersect(Target, Sheets("Daily Summary").Range("D27:D93")) Is Nothing Then
Cancel = True
CommentDetails.Show
End If
End Sub
a) How do I capture the dynamic cell address that triggered the userform load?
b) How do I offset three columns to the right, capture that cell's value and load it into the userform's input field named first_name?
Thanks to #Zwenn in the comments for pointing me in the right direction with Application.Caller. Updated code below, it executes but shows a Object Required error.
The name of the form is CommentDetails, the name of the input field is TextBoxArrival, both of which matches the code.
Private Sub Userform_initialize()
Me.TextBoxArrival.Value = Cells(Application.Caller.Row, Application.Caller.Column + 1)
'TextBoxArrival.Value = Cells(Application.Caller.Row, Application.Caller.Column + 1)
'MsgBox Cells(Application.Caller.Row, Application.Caller.Column + 1).Value, vbOKOnly
End Sub
I understand I have to declare application.caller along with the calling method, which in my case is Sub Worksheet_BeforeDoubleClick. Still getting the same error. I tried circumventing this by calling another separate sub before loading the userform.
Where do I define application.caller?
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim callingCellRow As Integer
If Not Application.Intersect(Target, Sheets("Daily Summary").Range("D27:D29")) Is Nothing Then
Select Case TypeName(Application.Caller)
Case "Range"
callingCellRow = Application.Caller.Row
callingCellColumn = Application.Caller.Column
Case "String"
callingCellRow = Application.Caller.Row
callingCellColumn = Application.Caller.Column
callingCellSheet = Application.Caller
Case "Error"
MySheet = "Error"
Case Else
MySheet = "unknown"
End Select
With CommentDetails
.Tag = callingCellRow '<~~ tell the UserForm there's something to bring in so that it'll fill controls from the sheet instead of initializing them
.Show
.Tag = ""
End With
Unload CommentDetails
End If
End Sub
There's 3 ways to do this explained on Daily Dose of Excel by Dick Kusleika (18 years ago!). I prefer the 3rd option as it handles the form instance with a variable.
In Worksheet_BeforeDoubleClick you can have this:
Option Explicit
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim objForm As UserForm1
If Not Application.Intersect(Target, Me.Range("B2:D2")) Is Nothing Then
Cancel = True
Set objForm = New UserForm1 ' <-- use a variable for the form instance
Set objForm.rngDoubleClicked = Target ' <-- set property of the form here with Target
objForm.Show
End If
End Sub
And then in the form code:
Option Explicit
Private m_rngDoubleClicked As Range
' set only
Public Property Set rngDoubleClicked(rng As Range)
Set m_rngDoubleClicked = rng
End Property
' use the property
Private Sub UserForm_Activate()
Dim strAddress As String
Dim rngOffset As Range
' m_rngDoubleClicked is now the range that was double clicked
strAddress = m_rngDoubleClicked.Parent.Name & "!" & m_rngDoubleClicked.Address
Set rngOffset = m_rngDoubleClicked.Offset(3, 0)
Me.TextBox1.Text = "The address of the double clicked cell is " & strAddress
Me.TextBox2.Text = "The value 3 rows down from double clicked cell is " & rngOffset.Text
End Sub
Private Sub UserForm_Initialize()
' no args for initialization unfortunately :(
End Sub
Example:

Show notification when cell value/outcome changes

I am trying to write a VBA code such that a Message box pops up whenever the value inside certain cells change.
The cells that I want to monitor change because they are linked to a query which is refreshed automatically. The VBA codes I found online only work when the cell value is changed manually, this will not work because the formula in the cells do not change, only the displayed value changes.
Can anyone help me with this?
Application Calculate (Public Variables)
The following is written for a non-contiguous range.
A message box pop's up each time a value in the range changes via formula.
Copy the codes into the appropriate modules.
Adjust the values in the constants section.
This is an automated solution. To start you should either save, close, and reopen the workbook or run the popupMsgBoxInit procedure.
Standard Module e.g. Module1
Option Explicit
Public Const popupWsName As String = "Sheet1"
Public Const popupRgAddress As String = "A1,C3,E5"
Public popupRg As Range
Public popupCount As Long
Public popupArr As Variant
Sub popupMsgBoxInit()
Set popupRg = ThisWorkbook.Worksheets(popupWsName).Range(popupRgAddress)
popupRg.Interior.Color = 65535 'xlNone
popupCount = popupRg.Cells.Count
ReDim popupArr(1 To popupCount)
Dim cel As Range
Dim i As Long
For Each cel In popupRg.Cells
i = i + 1
popupArr(i) = cel.Value
Next cel
End Sub
Sub popupMsgBox()
Dim chCount As Long
Dim cel As Range
Dim i As Long
For Each cel In popupRg.Cells
i = i + 1
If cel.Value <> popupArr(i) Then
chCount = chCount + 1
popupArr(i) = cel.Value
End If
Next cel
MsgBox "Number of Changes: " & chCount, vbInformation, "Success"
End Sub
ThisWorkbook Module
Option Explicit
Private Sub Workbook_Open()
popupMsgBoxInit
End Sub
Sheet Module e.g. Sheet1
Option Explicit
Private Sub Worksheet_Calculate()
popupMsgBox
End Sub

userform with vlookup function from different sheets when i click an optionbutton for each lookup value

I have to know, is this possible that the single combo box that had a lists from 2 different sheets by using option buttons. this works well. but the vlookup function is working for sheet 1 only not sheet 2.
explanation:
in my userform,
1 combobox = cmbbx1
2 option buttons = 1.hq 2.whs
2 textboxes = 1.txtbx1 2.txtbx2
When I click on the option button hq the list of sheet1 is shown in combobox. then another 2 textboxes already coded with Application.WorksheetFunction.Vlookup, so they're showing the given cell value.
but i can't make it work when i click on the option button whs. in this time combobox is showing the list from sheet2 but vlookup not working here.
here is the code what i get from another source for vlookup function.
Private Sub CmbBX1_AfterUpdate()
'Check to see if value exists
If WorksheetFunction.CountIf(Sheet2.Range("B:B"), Me.CmbBX1.Value) = 0 Then
MsgBox "Employee Not Registered"
Me.CmbBX1.Value = ""
Exit Sub
End If
'Lookup values based on control
With Me
.TxBx1 = Application.WorksheetFunction.VLookup(Me.CmbBX1, Sheet2.Range("Emp_ltl"), 2, 0)
.TxBx2 = Application.WorksheetFunction.VLookup(Me.CmbBX1, Sheet2.Range("Emp_ltl"), 3, 0)
End With
End Sub
This is the code I used for the Option buttons:
Option Explicit
Public myList As Variant
Private Sub hq_Click()
myList = ThisWorkbook.Worksheets("LTL").Range("Emp_ltl").Value
Me.CmbBX1.List = myList
End Sub
Private Sub whs_Click()
myList = ThisWorkbook.Worksheets("LTS").Range("Emp_ltS").Value
Me.CmbBX1.List = myList
End Sub
I believe something like the following will do it:
Private Sub CmbBX1_AfterUpdate()
If hq.Value = True Then 'check if hq is selected
Dim ws As Worksheets: Set ws = Worksheets("LTL") 'declare your worksheet and your range
Dim rng As Range: Set rng = ws.Range("Emp_ltl")
myList = ThisWorkbook.Worksheets("LTL").Range("Emp_ltl").Value
Me.CmbBX1.List = myList
ElseIf whs.Value = True Then 'if whs is selected
Dim ws As Worksheets: Set ws = Worksheets("LTS") 'declare and set your worksheet and range
Dim rng As Range: Set rng = ws.Range("Emp_ltS")
myList = ThisWorkbook.Worksheets("LTS").Range("Emp_ltS").Value
Me.CmbBX1.List = myLis
Else
MsgBox "No Option has been selected"
Exit Sub
End If
'Check to see if value exists
If WorksheetFunction.CountIf(ws.Range("B:B"), Me.CmbBX1.Value) = 0 Then
MsgBox "Employee Not Registered"
Me.CmbBX1.Value = ""
Exit Sub
End If
'Lookup values based on control
With Me
.TxBx1 = Application.WorksheetFunction.VLookup(Me.CmbBX1, rng, 2, 0)
.TxBx2 = Application.WorksheetFunction.VLookup(Me.CmbBX1, rng, 3, 0)
End With
End Sub

VBA is it possible to pass a Dictionary/Collection to an autofilter?

The idea was to create a variable that would save the changes made to it from previous use of the macro. I have a userform that pulls values from a range and populates unique values in a listbox. I then want to be able to add selected values to my dictionary/collection and save the change. Once all necessary changes have been made, the macro should use the dictionary variable as criteria for an autofilter.
My question is two fold, what class should I use to accomplish this? How can a use this variable to autofilter my worksheet? Userform code is below:
The First bit of code is for the "Add" command button. It is supposed to take the selected value(s) in the listbox and add them to the dictionary titled "Market". The code after that pulls the values from a recently opened excel workbook an displays unique values in the listbox. Listbox2 holds all previous values from past uses of the macro. I want to add a "Delete" button to the userform to tidy up the list if necessary. The two public variables below are actually located on the main macro module, this would allow me to store the values in the dictionary after the userform has stopped running.
Private Sub CommandButton1_Click()
Dim i As Long
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) = True Then
Market.Add ListBox1.List(i)
Set Market = New Collection
End If
Next
End Sub
Private Sub UserForm_Initialize()
Dim myList As Collection
Dim myRange As Range
Dim ws As Worksheet
Dim myVal As Variant
Dim Col As Integer
Set ws = ActiveWorkbook.Sheets("Daily Unconfirmed")
Col = WorksheetFunction.Match("Marketer", ws.Range("3:3"), 0)
Set myRange = ws.Range(Cells(4, Col), Cells(4, Col).End(xlDown))
Set myList = New Collection
On Error Resume Next
For Each mycell In myRange.Cells
myList.Add mycell.Value, CStr(mycell.Value)
Next mycell
On Error GoTo 0
For Each myVal In myList
Me.ListBox1.AddItem myVal
Next myVal
Public item As Variant
Public Market As Collection
Market.Add "Al D"
Market.Add "B Collins"
Market.Add "B G"
Market.Add "C Huter"
For Each item In Market
Me.ListBox2.AddItem item
Next item
End Sub
Since AutoFilter runs from an array, I would build the array dynamically and use it in a filtering sub:
Dim ary()
Sub MAIN()
Call BuildDynamicArray
Call FilterMyData
End Sub
Sub BuildDynamicArray()
Dim inString As String
i = 1
While 1 = 1
x = Application.InputBox(Prompt:="Enter a value", Type:=2)
If x = False Then GoTo out
ReDim Preserve ary(1 To i)
ary(i) = x
i = i + 1
Wend
out:
End Sub
Sub FilterMyData()
ActiveSheet.Range("$A$1:$A$10").AutoFilter Field:=1, Criteria1:=ary, Operator:=xlFilterValues
End Sub

How to assign a name to an Excel cell using VBA?

I need to assign a unique name to a cell which calls a particular user defined function.
I tried
Dim r As Range
set r = Application.Caller
r.Name = "Unique"
The following code sets cell A1 to have the name 'MyUniqueName':
Private Sub NameCell()
Dim rng As Range
Set rng = Range("A1")
rng.Name = "MyUniqueName"
End Sub
Does that help?
EDIT
I am not sure how to achieve what you need in a simple way, elegant way. I did manage this hack - see if this helps but you'd most likely want to augment my solution.
Suppose I have the following user defined function in VBA that I reference in a worksheet:
Public Function MyCustomCalc(Input1 As Integer, Input2 As Integer, Input3 As Integer) As Integer
MyCustomCalc = (Input1 + Input2) - Input3
End Function
Each time I call this function I want the cell that called that function to be assigned a name. To achieve this, if you go to 'ThisWorkbook' in your VBA project and select the 'SheetChange' event then you can add the following:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Left$(Target.Formula, 13) = "=MyCustomCalc" Then
Target.Name = "MyUniqueName"
End If
End Sub
In short, this code checks to see if the calling range is using the user defined function and then assigns the range a name (MyUniqueName) in this instance.
As I say, the above isn't great but it may give you a start. I couldn't find a way to embed code into the user defined function and set the range name directly e.g. using Application.Caller.Address or Application.Caller.Cells(1,1) etc. I am certain there is a way but I'm afraid I am a shade rusty on VBA...
I used this sub to work its way across the top row of a worksheet and if there is a value in the top row it sets that value as the name of that cell. It is VBA based so somewhat crude and simple, but it does the job!!
Private Sub SortForContactsOutlookImport()
Dim ThisCell As Object
Dim NextCell As Object
Dim RangeName As String
Set ThisCell = ActiveCell
Set NextCell = ThisCell.Offset(0, 1)
Do
If ThisCell.Value <> "" Then
RangeName = ThisCell.Value
ActiveWorkbook.Names.Add Name:=RangeName, RefersTo:=ThisCell
Set ThisCell = NextCell
Set NextCell = ThisCell.Offset(0, 1)
End If
Loop Until ThisCell.Value = "Web Page"
End Sub
I use this sub, without formal error handling:
Sub NameAdd()
Dim rng As Range
Dim nameString, rangeString, sheetString As String
On Error Resume Next
rangeString = "A5:B8"
nameString = "My_Name"
sheetString = "Sheet1"
Set rng = Worksheets(sheetString).Range(rangeString)
ThisWorkbook.Names.Add name:=nameString, RefersTo:=rng
End Sub
To Delete a Name:
Sub NameDelete()
Dim nm As name
For Each nm In ActiveWorkbook.Names
If nm.name = "My_Name" Then nm.Delete
Next
End Sub

Resources