vba Excel do while user input - vba

I'm working on some VBA scripting for Excel and having some trouble. What I'm trying to do let the user select a set of columns to concatonate. So, i would use Application.InputBox in a loop to store the column headers as a range, then take that range to format a concatonate() formula.
E.g.: the user wants to concatonate the contents of colum d, e, a (in that order). The InputBox would continue to appear until the user clicked cancel, storing the range (the column header such as d1, e1, a1) in an array from which I can build the concatonate statement.
I've spent some time with this and am stumped. My first hurdle is that I can't seem to get a while loop that ends when the InputBox returns false (user presses "cancel").
Any help would be really appreciated. Having a C based background, I'm really struggling with VBA...

even though you did not provide any code try doing this...
Dim concatCells()
Dim i As Integer
i = 0
msgselect = MsgBox("Please select the cell you wish to concatenate.", vbOKCancel)
Do While msgselect = vbOK
i = i + 1
ReDim Preserve concatCells(i)
concatCells(i - 1) = Application.InputBox("select cell", Type:=8)
msgselect = MsgBox("Please select the cell you wish to concatenate.", vbOKCancel)
Loop
Dim rangeToConcat As String
For i = 0 To UBound(concatCells)
rangeToConcat = rangeToConcat & concatCells(i)
Next i
Range("A1").Formula = rangeToConcat

Related

How to reflect a date stamp log that does not overwrite the one before?

I have an Excel Sheet that I am using to track the status of hiring, where each row is a vacancy's record and one of the cells is a drop down list to change the status of that specific vacancy. Statuses are for example (Advertising, Interviewing, Hired)
What I wanted to do is once I click a status, by the end of the row (let's say last of my record here is Column S, so when I click advertising, a date stamp goes on S and the status itself gets printed there on Column T, and if I choose the 2nd status, it would go on Columns U & V and so on.
What I used so far is something different, that reflects the date stamp based on the corresponding column of that status:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim WorkRng As Range
Dim Rng As Range
Dim xOffsetColumn As Long
Set WorkRng = Intersect(Application.ActiveSheet.Range("H:H"), Target)
If Not WorkRng Is Nothing Then
Application.EnableEvents = False
For Each Rng In WorkRng
Select Case Rng.Value2
Case "Not-initiated"
xOffsetColumn = 100 'Column O
Case "Adv/Sourcing"
xOffsetColumn = 9 'Column U
Case "Interviewing"
xOffsetColumn = 10 'Column Q
Case "Offering & Selection"
xOffsetColumn = 11 'Column R
Case "Onboarding"
xOffsetColumn = 12 'Column S
Case "Contract Signed"
xOffsetColumn = 13 'Column U
Case "Joined"
xOffsetColumn = 14 'Column Q
Case Else
xOffsetColumn = 101 'Column T - entry not listed above
End Select
Rng.Offset(0, xOffsetColumn).Value = Now
Rng.Offset(0, xOffsetColumn).NumberFormat = "dd-mm-yyyy"
Next
Application.EnableEvents = True
End If
End Sub
If I understand properly, you want a Drop Down on each row, around column R, with 3 choices, that when selected will populate the cell 1, 2, or 3 cells to the right, with the current date.
What I would do is this: if I'm going to place a Drop Down on top of cell R2, then size it to be exactly the same as the cell* (see bottom of this answer) and hide the Cell Link right underneath it, $R2.
Assign a macro, pointing to this sub in a module:
Sub DropDown2_Change() '(all the drop downs call this sub, on change)
Dim dd As Shape, ddCell As String, ddValue As String, ddIndex As Integer
Set dd = ActiveSheet.Shapes(Application.Caller) 'shape object: the selected DropDown
ddValue = dd.ControlFormat.List(dd.ControlFormat.ListIndex) 'dropdown's new string value [not used]
ddCell = dd.ControlFormat.LinkedCell 'range object: the cell linked to the dropdown
ddIndex = ActiveSheet.Range(ddCell) 'selected index: 1=Advertised,2=Interviewed,3=Hired
Range(ddCell).Offset(0, ddIndex) = Date 'set date of cell 1,2,or 3 cells to the right
End Sub
Sometimes it can be a pain just getting the Drop Downs to line up with the cells properly (especially if you start moving around columns afterwards) but in the past I found it best to create & setup the Drop Downs programmatically, to ensure perfect alignment, correct naming, etc. (In fact, any time I do need to move them after that, I just delete and re-create them ALL, to save a headache.)
Depending on your needs you might be able to get away with copying the 1st completed, functioning Drop Down manually, and pasting it one by one into each cell below. Just make sure that the Cell Link is Abs/Rel like **$**B2 or else they all might default to the same Cell Link.
It's okay if they all share the same sub since the code above will check for the Cell Link of the changed Drop Down.
This way you won't have to mess around with WorkSheet_Change (which doesn't fire for Drop Down changes anyway).
You can download the test sheet I used from JumpShare here: tmpDropDowns.xlsm. (It views online but VBA won't work unless you download it.)
Let me know if you have any questions!
UPDATE:
To populate first empty cell to the right of the Drop Down with the Date + Status (as opposed to populating only columns S,T,U).
Updated Code:
Option Explicit
Sub DropDown2_Change() '(all the drop downs call this sub, on change)
Dim dd As Shape, ddCell As String, ddValue As String, ddIndex As Integer
Set dd = ActiveSheet.Shapes(Application.Caller) 'shape object: the selected DropDown
ddValue = dd.ControlFormat.List(dd.ControlFormat.ListIndex) 'dropdown's new string value
ddCell = dd.ControlFormat.LinkedCell 'range object: the cell linked to the dropdown
ddIndex = ActiveSheet.Range(ddCell) 'selected index: 1=Advertised,2=Interviewed,3=Hired [not used]
FirstEmptyCellToRight(Range(ddCell)) = ddValue & " " & Date 'set date of cell 1,2,or 3 cells to the right
End Sub
Function FirstEmptyCellToRight(cell_In As Range) As Range
'since ".End(xlToRight).Offset(0, 1)" wasn't working for me
'returns cell_In if it's blank, and if not then the first blank cell to the right
Set FirstEmptyCellToRight = cell_In
Do Until IsEmpty(FirstEmptyCellToRight) Or FirstEmptyCellToRight.Value = ""
Set FirstEmptyCellToRight = FirstEmptyCellToRight.Offset(0, 1)
Loop
End Function
Adding screenshot:
Updated File on JumpShare: tmpDropDowns.xlsm
(Must be downloaded; viewing online won't work with VBA.)

VBA Form - Vlookup cell and assign value to that cell

Encountering an issue in a VBA regarding vlookup function.
I have 2 comboboxes and 6 Textboxs for user input.
I want to use a vlookup (or index,Match(),Match()) to look up a cell in a data table and assign the values from the textboxes to these cells.
When I run the code for what I believe should work, it is returning object errors.
Private Sub CommandButton2_Click()
Dim MonthlyTable As Range
Set MonthlyTable = Sheets("DATA Monthly").Range("A6:AE400")
Dim ColumnRef As Range
Set ColumnRef = Sheets("Drivers").Range("N11")
' Assign CB2 value to M11 cell reference so it can be converted to a column ref in N11.
Sheets("Drivers").Range("M11").Value = ComboBox2.Value
Dim CB1Value As String
CB1Value = "Joiners" & ComboBox1.Value
Dim CB2Value As String
CB2Value = ComboBox2.Value
MsgBox CB1Value & " " & CB2Value
Dim tb1value As Range
tb1value = Application.WorksheetFunction.VLookup(CB1Value, MonthlyTable, ColumnRef, False)
tb1value.Value = TextBox1.Value
Unload Me
End Sub
I am at a loss for what to do here as I feel like it should be this simple!
Thanks in advance.
Edit. Further digging indicates that you cannot select a cell you are vlookup'ing as this commands only returns a value it does not actually select the cell for my intents and purposes.
not really clear to me you actual aim, but just following up your desire as stated by:
I want to use a vlookup (or index,Match(),Match()) to look up a cell
in a data table and assign the values from the textboxes to these
cells
you may want to adopt the following technique:
Dim tb1value As Variant '<--| a variant can be assigned the result of Application.Match method and store an error to be properly cheeked for
tb1value = Application.Match(CB1Value, MonthlyTable.Column(1), 0) '<--| try finding an exact match for 'CB1Value' in the first column of your data range
If Not IsError(tblvalue) Then MonthlyTable(tb1value, columnRef.Value).Value = TextBox1.Value '<--| if successful then write 'TextBox1' value in data range cell in the same row of the found match and with `columnRef` range value as its column index
Excel uses worksheet functions to manipulate data, VBA has different tools, and when you find yourself setting cell values on a sheet via VBA so that some worksheet function can refer to them it is time to look for a true VBA solution. I suggest the following which, by the way, you might consider running on the Change event of Cbx2 instead of a command button.
Private Sub Solution_Click()
' 24 Mar 2017
Dim MonthlyTable As Range
Dim Rng As Range
Dim Lookup As String
Dim Done As Boolean
Set MonthlyTable = Sheets("DATA Monthly").Range("A2:AE400")
' take the lookup value from Cbx1
Lookup = ComboBox1.Value
Set Rng = MonthlyTable.Find(Lookup)
If Rng Is Nothing Then
MsgBox Chr(34) & Lookup & """ wasn't found.", vbInformation, "Invalid search"
Else
With ComboBox2
If .ListIndex < 0 Then
MsgBox "Please select a data type.", vbExclamation, "Missing specification"
Else
TextBox1.Value = MonthlyTable.Cells(Rng.Row, .ListIndex + 1)
Done = True
End If
End With
End If
If Done Then Unload Me
End Sub
There are two points that need explanation. First, the form doesn't close after a rejected entry. You would have to add a Cancel button to avoid an unwanted loop where the user can't leave the form until he enters something correct. Note that Done is set to True only when the search criterion was found And a value was returned, and the form isn't closed until Done = True.
Second, observe the use of the ListIndex property of Cbx2. All the items in that Cbx's dropdown are numbered from 0 and up. The ListIndex property tells which item was selected. It is -1 when no selection was made. If you list the captions of your worksheet columns in the dropdown (you might do this automatically when you initialise the form) there will be a direct relationship between the caption selected by the user (such as "Joiners") and the ListIndex. The first column of MonthlyTable will have the ListIndex 0. So you can convert the ListIndex into a column of MonthlyTable by adding 1.
I think it is better to use "find" in excell vba to select a cell instead of using vlookup or other methods.

find out cell address having = sheet name

I'm just a beginner for VBA but advance in MS excel. that's why I am very much interested to learn VBA.
ok this is my first question here
Actully i need to format excel sheet where file name is = sheet1 name and it is somewhere in column "A" so I want to select & delete all the rows above this cell & below untill there is a blank cell/row.
I have tried much with InStr & find function but no succeed. Also try to find cell address like B5 but could no do that.
Welcome to StackOverflow. As you have already been informed by newguy, when posting a question you should also show what you have tried so far... some piece of code, printscreens, etc.
Your explanation was not that clear (at least to me), but based on what I have understood, I have made a small code sample for you to get you started. I have broken down the code into the function blocks, so that you can better understand what they are trying to achieve.
Here is the code:
'the following function will find cell with the specific text
Private Function FindCell(ws As Worksheet, strToSearch As String, Optional sColumn As String = "A") As Integer
Dim iCounter As Integer
'as you do not know where exactly it exists, we loop from first cell in the particular row
'to the very last celll
With ws
For iCounter = 1 To .Range("A65000").End(xlUp).Row ' or .UsedRange.Rows.Count, or any other method
If .Range(sColumn & iCounter).Value = strToSearch Then
'yay, we have found the cell!
'pass out the reference
FindCell = iCounter
'now call exit function as we no longer need to continue searching for the cell (we have already found it!)
Exit Function
End If
Next iCounter
End With
'in case the cell does not exist, we can return -1
FindCell = -1
End Function
'the following cell will search the very first cell to the top (starting from specific row), which is blank / empty
Private Function FindEmptyCell(ws As Worksheet, iStartRow As Integer, Optional sColumn As String = "A") As Integer
'This function does the same, as if you have selected specific cell
'and then pressed left Ctrl + Up arrow at the same time
'Try it!
'You can do the same with Right + Left + Bottom arrow as well
FindEmptyCell = ws.Range(sColumn & iStartRow).End(xlUp).Row + 1
End Function
Private Sub EraseRows()
Dim iStartCell As Integer
Dim iEndCell As Integer
On Error GoTo 0
'First let's find the "bottom" cell which is the cell with the specific text you are looking for
iEndCell = FindCell(ActiveSheet, "TextIAmLookingFor")
'now let's see find the top blank cell (so that we get the range of from - to that you want to erase)
iStartCell = FindEmptyCell(ActiveSheet, iEndCell)
'now we can delete the rows!
'iEndCell-1 because you don't want to erase the cell with your search string, right?
ActiveSheet.Rows(CStr(iStartCell) & ":" & CStr(iEndCell - 1)).EntireRow.Delete (xlUp)
End Sub

dynamically select sheet from the inputprompt and print to a file

i have 3 sheets in my workbook namely sheet1 ,sheet3 and sheet2 when i enter a values from in the inputpromt say (eg:1,2 ) i split it and store it in an array it must dynamically select the sheet1 and sheet2 and print values to a file.
i have done a pseudo below can any one
Sub example()
Dim Ran As Range
Dim cnt as String
Open "D:\temp\test.txt" For Output As #1
Dim myarray() As String
Dim holder As String
dim ab as Strig
ab="this is sample data"
holder = InputBox("Enter your choice eg:1,2")
myarray = Split(holder, ",")
Dim name, country,birth As String
For i = 1 To UBound(myarray)
If i = 1 Or i = 2 Then
name = Sheets(i).Range("Z12").Value
country= Sheets(i).Range("PQ26").Value
birth = Sheets(i).Range("ab24").Value
ab=ab & name & country & birth
Print #1, ab
End If
Next i
end Sub
****in the inputbox if i give value as 1,2 then it must select values from sheet 1 and sheet2****
I am guessing you that you are trying to understand InputBox and how to split a string such as “1, 2” and process the separate values as sheet numbers.
Sheet numbers mean nothing to the user so I do not believe this is a good approach. You need to offer the user a list of worksheet tabs from which they can select.
There is InputBox and Application.InputBox. These are different and I do not like either. They come from the earliest versions of VB and were probably originally a direct match to MS-DOS’s console input. With one exception, there are much better approaches. The one exception is the ability to input a range with Application.InputBox for which I do not know a convenient alternative.
In the code below I use InputBox. You may think I have overdone the validation but what if the user enters “A”? Your code would assume “A” was a sheet number but Excel would assume it was a worksheet name and stop execution with a subscript error. You must check for any error that will cause your code to stop with a run-time error.
Instead of either InputBox you should use a User Form. There are on-line tutorials to get you started. With a User Form you have several choices including:
A List box, with multiple selection enabled, containing the names of every permitted worksheet.
A column of radio buttons, one per permitted worksheet. These would be unlinked so the user could select several.
A command button for each permitted worksheet which the user could click in turn.
All of these choices are much user-friendlier than InputBox.
Step through the code below with F8 and study what it does. Come back with questions if necessary but the more you can understand on your own the faster you will develop.
Option Explicit
' Please indent your macros consistently. This makes them much easier ri read
Sub example()
Dim Ran As Range
Dim cnt As String
Dim myarray() As String
Dim ab As String
' Variables I have added or which are replacements for yours.
Dim Answer As String
Dim AllValuesGood As Boolean
Dim InxMA As Long
Dim InxSht As Long
Dim Question As String
' I like to keep all my Dim statements together at the top.
' This makes them easier to find.
Dim name, country, birth As String
' I have output to the Immediate window which is simpler when
' experimenting.
'Open "D:\temp\test.txt" For Output As #1
Question = "Enter your choice eg: ""1"" or ""1,2"""
' You omitted the code to check the Answer
Do While True
Answer = InputBox(Question, "Experiment with Input box")
' Have a string but user could have typed anything
If Answer = "" Then
' Cannot tell if user has clicked Cancel or if user clicked Return
' before entering value. Have to assume Cancel
Debug.Print "User clicked Cancel"
Exit Sub
Else
AllValuesGood = True ' Assume good answer until find otherwise
myarray = Split(Answer, ",")
For InxMA = 0 To UBound(myarray)
If IsNumeric(myarray(InxMA)) Then
InxSht = Val(myarray(InxMA))
If InxSht < 1 Or InxSht > 2 Then
' Sheet number outside permitted range
AllValuesGood = False
Exit For
End If
Else
' Non-numeric sheet number
AllValuesGood = False
'Debug.Assert False
Exit For
End If
Next
End If
If AllValuesGood Then
' Have good answer. Exit Do-Loop to print sheets
Exit Do
Else
' An invalid sheet number has been found
Question = "Click cancel to exit or enter ""1"" or ""2"" or ""1, 2"""
' Loop to repeat Input
End If
Loop ' Until have good answer
' If get here Answer is a list of good sheet number
For InxMA = 0 To UBound(myarray)
InxSht = Val(myarray(InxMA))
' I have not created sample worksheets with data in Z12, PQ26 and ab24
' but this shows the code you need
'With Worksheets(InxSht)
' name = .Range("Z12").Value
' country = .Range("PQ26").Value
' birth = .Range("ab24").Value
'Next
name = "Name" & InxSht
country = "Country" & InxSht
birth = "Birth" & InxSht
' Have to initialise ab here because need new value per sheet
ab = "this is sample data"
ab = ab & name & country & birth
Debug.Print ab
' The value of ab will be messy because you have no spaces between words.
'Print #1, ab
Next InxMA
End Sub

excel - find and highlight text from one sheet on second sheet

I am getting killed by excel, I'm not 100% sure it can do exactly what I'm needing. I've tried various functions and can come close, but none are perfect. I've uploaded a spreadsheet as an example. I have a sheet of mailboxes, followed by a cell with the users who have access to the mailbox. The cell has anywhere from 0 to 5 users separated by commas. The second sheet has a list of users. What I need is a way to parse out the first sheet, either highlight on the first sheet, or copy to another sheet; all the mailboxes that all the associated users match in the second cell appear on the second sheet.
The real world sheet I have has over 2500 mailboxes with as many as 205 (as few as 0) associated users, so I desperately need a way to mechanically filter the sheet. I'm trying to filter the mailboxes that all the associated users are present on a second sheet.
I've tried using vlookup, index/match and a few others, and what seems to trip it up is having the comma separation. Using ""& cell_i'm_looking_for &"" returns nothing so I'm guessing I need to try something else. I also have the sheet with all the users in separate cells.
I downloaded your sheet and created a module with the following function inside of it:
Function mymailboxes(who As Range, lookup As Range)
Dim myRow As Range
For Each myRow In lookup
If InStr(myRow.Cells(1, 2).Value, who.Value) > 0 Then
myReturn = myReturn & "," & myRow.Cells(1, 1).Value
End If
Next
'cut off the first ,
myReturn = Right(myReturn, Len(myReturn) - 1)
mymailboxes = myReturn
End Function
Then on Sheet 2, Cell E2 I gave the following formula: =mymailboxes(A2, Sheet1!A1:B50) which gave me the following results: mailbox1,mailbox2,mailbox4,mailbox8,mailbox12,mailbox17,mailbox21,mailbox25,mailbox28,mailbox34,mailbox39,mailbox41,mailbox42,mailbox44,mailbox49,mailbox50
I hope this helps.
To get a list of invalid users the following function will help.
Function get_invalid_users(users As Range, validusers As Range)
Dim myRow As Range
myusers = Split(users, ",")
myReturn = ""
For Each user In myusers
is_valid_user = False
'Guilty until proven innocent
For Each myRow In validusers
If myRow.Cells(1, 1).Value = user Then
is_valid_user = True
'Proven innocent, break out of the loop, no double jeopardy.
Exit For
End If
Next
If is_valid_user = False Then
myReturn = myReturn & "," & user
End If
Next
If Len(myReturn) > 0 Then
myReturn = Right(myReturn, Len(myReturn) - 1)
Else
myReturn = ""
End If
get_invalid_users = myReturn
End Function
Sheet 1, Cell C2 with formula: =get_invalid_users(B2, Sheet2!$A$1:$A$3) returned zx1234