Populating a list box with data from worksheet - excel

I currently get the following error when I run the code (shown below)
Type Mismatch
To give a bit of context, the worksheet CourseSelection has row 3 populated from A to F. I would like to put the entries from A2:A6 into a listbox. However, I want to generalize this process and make it dynamic to include additional categories if they are added after column F. Therefore I need an automatic way to do this through code similar to what I have below. However, I am getting error messages and I am unsure why.
I defined TaskList as a Range prior to this code. When I hover over xlToRight when I run the code I see a very large negative value (-4191). I am unsure if this is part of the problem.
With Worksheets(CourseSelection).Range("A3")
Set TaskList = Range(.Offset(0, 1), .End(xlToRight))
End With
frmTaskSelection.lbTasks.RowSource = TaskList

Unless you have CourseSelection defined as a constant returning an existing worksheet name then the code will fail on With Worksheets(CourseSelection).Range("A3"). If you want to work with a sheet name CourseSelection you would use With Worksheets("CourseSelection").Range("A3").
Given you error message though you appear to have gotten past this point and your code appears to be failing on frmTaskSelection.lbTasks.RowSource = TaskList. This is because RowSource expects an address
If you were looking to populate the values from a sheet called CourseSelection from A3 to Ax where x is the last used cell, then this code will work from any active sheet.
Please note thate I was unclear as to how you wanted to use further values from column F in addition to A2:A6. If you can provide further guidance/picture etc then the code below can be adapted to suit
Sub test()
Dim ws As Worksheet
Dim rng1 As Range
Set ws = Worksheets("CourseSelection")
Set rng1 = ws.Range(ws.[a3], ws.Cells(Rows.Count, "A").End(xlUp))
frmTaskSelection.lbTasks.RowSource = "'" & ws.Name & "'!" & rng1.Address
frmTaskSelection.Show
End Sub

Related

Matrix to create sheets and copy certain information

I have a unit matrix that illustrates work items for an apartment complex. It expands to 5 floors and has more work items than just the kitchen scope. My end goal is to have a sheet for each unit, listing the specific items needed for that unit. It would be very helpful once construction begins.
I want to do 2 things. 1 - Create new sheets for each unit (C5:C124) using the template. 2 - Copy over the information based on what is marked with an "X"
I know how to create a macros that will create blank sheets from the number of units I have. I'm stuck on integrating the template.
Thank you for reading.
Unit Scope Matrix
Template
Edit 1:
Here is my new code that can take a range of room#s and create new sheets from it. Now I would like to copy and paste the the row next the the according room# cell and paste in the appropriate sheet.
Sub CreateSheets()
Dim rng As Range
Dim cell As Range
On Error GoTo Errorhandling
'Creates popup box asking for the room numbers
Set rng = Application.InputBox(Prompt:="Select cell range:", _
Title:="Create sheets", _
Default:=Selection.Address, Type:=8)
For Each cell In rng
'Check if cell is not empty
If cell <> "" Then
'Insert worksheet and name the worksheet based on cell value
Sheets("Template").Copy After:=Sheets("Unit Types")
'Name new sheet based off two cells on Bid Summary List Cells (Bi and Di)
ActiveSheet.Name = "UNIT-" & cell
'This is where I think I should add the copy/paste lines... but I don't know how.
'Copy unit# row and paste in correct worksheet
'Range("XX:XX").Copy Range("XX:XX")
End If
'Continue with next cell in cell range
Next cell
'Go here if an error occurs
Errorhandling:
'Stop macro
End Sub
The code to copy the template and rename it is easy enough to make. Start recording, do one manually, stop recording, then press Alt-F11 to see how it's done, then steal from that to make your own function.
I suspect you'll end up with something that looks like
Function NewSheet(nm as String) as Worksheet
Dim template As Worksheet
set template = ActiveWorkbook.sheets('template')
set NewSheet = template.copy(ActiveWorkbook)
NewSheet.name = nm // change the tab name
NewSheet range('B1') = nm // add to the sheet as well
End Function
(warning: syntax and method names might unintentionally be wrong, this is intended to give you a head start, not do it for you)
and then you'll need to write a macro that loops through column C and calls your function. In this case you can ignore that NewSheet returns a new Worksheet object, but this way provides flexible code for future needs. Also, by isolating what you need to do to make a new worksheet as its own function that's called multiple times, It's easier to reason through and test, and the looping code that calls it is much easier to read as well.
Try searching for "Excel VBA looping examples" to get a head start on looping if you are unfamiliar.

VBA Vlookup not finding values that exist

I am working with two different Worksheets in one workbook. My task is to look up the model# of a product from Sheet1, find that same model# in Sheet2, and get the cost of that product, which is located a few columns away.
So naturally, I tried to use Vlookup, because that function is enough for this query.
I will post my code below, and then explain the problems I am facing. I am new to VBA and have searched many many different Stack posts, and tried the various solutions, to no avail.
Private Sub CommandButton1_Click()
Dim tbdCell As Range
Dim model As Range
Dim cell As Range
Dim PAsheet As Worksheet
Dim DB As Worksheet
Dim target As Variant
Set DB = Worksheets("Database")
Set PAsheet = Sheets("Pricing Agreement")
Set tbdCell = Range("N2:N4700")
On Error GoTo ErrHandler:
For Each cell In tbdCell
Set model = cell.Offset(0, -6)
cell = WorksheetFunction.VLookup((CStr(model)), PAsheet.Range(CStr("C2:D2000")), 6, True)
Next cell
Exit Sub
ErrHandler:
Select Case Err.Number
Case 0
Case 1004
cell = "missing"
Resume Next
Case Else
MsgBox Err.Number & vbNewLine & Err.Description
Exit Sub
End Select
End Sub
So upon debugging and testing, most things work until we get to the line where I use the Vlookup function. I invariably get error 1004, even though the data exists in the other spreadsheet. So the cells that I need to fill will always fill with "missing" as posted above in the Error Handling Code.
I tried using the Application version of the function. I tried using different variables and declaring them as Variant type. I even tried making the table_array range just one row with 2 column coverage, in an attempt to force a match for one particular model #. So far, to avoid a type mismatch, I cast 'model'(the model#) into a String, and I also cast the search range in PAsheet to String. The final thing I tried was to not search for an exact match(last argument was set to true)
So in anticipation of future questions about the data that the Vlookup is based on, I will include necessary information about how both sheets are formatted.
Info that you may need:
We start in column N, where the prices are missing in Sheet1(Database).
I set model to the value in the same row, 6 columns to the left(Column H).
Testing with MsgBox proved this to work for me, and on debug, the model variable displays the correct info, so this isn't the issue.
In PAsheet, the model #s are in column C. Originally I made the search table from C2:C2000 or so, but I was led to believe that you need at least a two column table for Vlookup to work, so I changed C2000 to D2000. Now the search range is a two column table.
In PAsheet, the cost of the product is in Column H, which is 5 away from column C. I need this value, so I put 6 in the column_index argument. It was 5 before, because I thought that you didn't count the first column, but I fixed that.
Finally I mostly tested with "False" as the last argument, but either way it doesn't work.
So after trying more than two dozen variations and strategies, I still get "missing" in the cells that I need to fill.
So, what am I doing wrong here? Thanks in advance.
If you are trying to return the 6th value from Column C your range needs to be updated to `PAsheet.Range("C2:H2000")
cell.Value = WorksheetFunction.VLookup(cell.Offset(, -6), PAsheet.Range("C2:H2000"), 6, False)

vba column address from column number

I have a column number , say columnNumber = 4 . I need the used range of this column. I know how to find the last used row, and I could convert the column number to a column number like so
ColumnLetter = Split(Cells(1, ColumnNumber).Address, "$")(1)
LastRow = sht.Cells(sht.Rows.Count, ColumnLetter).End(xlUp).Row
and then build an address like so
rngaddy = ColumnLetter & "1:" & ColumnLetter & LastRow
and finally do
Range(rngaddy)
But is there an easier way to find the complete used range of a column given it's number ?
Dim rngaddy As Range
With Sheet1
Set rngaddy = .Range(.Cells(1, 4), .Cells(.Rows.Count, 4).End(xlUp))
End With
and if, for some reason, you want to see the address in A1 notation, merely:
debug.print rngaddy.address
Note that in doing it this way, rngaddy is, itself, the range object and not a string. So no need to do Range(rngaddy)
You could return the last populated cell is in columns # col with this:
MsgBox Cells(sht.Rows.Count,col).End(xlUp).Address
If you want to return the first populated cell as well, you could use:
MsgBox IIf(IsEmpty(Cells(1,col)),Cells(1,col).End(xlDown),Cells(1,col)).Address
Therefore this would return only the "used" range of Column #4 (D):
Sub Example_GetUsedRangeOfColumn()
Const col = 4
Dim sht As Worksheet
Set sht = Sheets("Sheet1")
MsgBox Range(IIf(IsEmpty(Cells(1, col)), Cells(1, col).End(xlDown), _
Cells(1, col)), Cells(sht.Rows.Count, col).End(xlUp)).Address
End Sub
So with this example:
...the above procedure would return: .
My preferred method is to use ListObjects aka Excel Tables to hold any input data whenever I possibly can. ListObjects are named ranges that Excel automatically maintains on your behalf, and because they grow automatically when new data is added, they give you a very robust way of referencing ranges in Excel from VBA, that is more immune to users doing things that might otherwise break code reliant on the .End(xlUp) approach.
? Range("MyTable").ListObject.ListColumns("Column 1").DataBodyRange.Address
$A$3:$A$7
Often I'll give the column concerned a named range of its own, in case the user (or a developer) later wants to change the Table column name, and use that name in my code instead.
? Range("FirstColumn").Address
$A$3:$A$7
If somebody (perhaps me) adds rows/columns above/left of the range of interest or shuffles the order of Table columns around, or changes the name of a column, the code still references the intended range and doesn't need to be changed.
? Range("FirstColumn").Address
$C$4:$C$8
? Range(Range("FirstColumn").Address & ":" & Range("FirstColumn").EntireColumn.cells(1).address).Address
$C$1:$C$8
Granted, that method of getting the range from the top cell (which may be above the ListObject) to the bottom of the column concerned is kinda long, but once you start using ListObjects more in your code you normally don't care what is above or below them...you just want the goods held inside.
I haven't used .End(xlUp) in years, other than to find where my data ends should I be in the process of turning it into a ListObject. But I'm a ListObject evangelist...your mileage may vary :-)
to get the real UsedRange of a columns you could use:
With Columns(columnNumber).SpecialCells(xlCellTypeConstants)
Set rngaddy = .Parent.Range(.Areas(1), .Areas(.Areas.Count))
End With
where rngaddy is a Range object
of course what above would fail if the column has no "constant" cells, then you may want to add some error trapping or entry check (e.g. If WorksheetFunction.CountA(Columns(columnNumber)) = 0 Then Exit Sub
Or
Option Explicit
Public Sub test()
Const columnNumber As Long = 4
Dim rngaddy As Range
Set rngaddy = Intersect(Columns(2), ActiveSheet.UsedRange): Debug.Print rngaddy.Address
End Sub

Selecting a Named Range Only to Last Row

I'm pulling raw data into a template, but I have no guarantee that between teams they'll be using the same UI template to pull the data. I can check to make sure the data I want is there, but they might be in different columns between teams.
So, my macro names the ranges (each is a full column based on the header name) and then will, hopefully, go to select desired named ranges to copy and paste.
Of course, these data sets are various sizes and I feel like I'm tripping over a super simple thing.
What I want to do is select my named range, but only to the last row, and then paste the values into my template. Except, I can't figure out how to only select to the last row.
Here's my current bit of troublesome code:
Dim LastRow as Long
LastRow = ActiveSheet.Cells(Rows.Count, "U").End(xlUp).Row
Range("PullPlacementname" & LastRow).Copy
Running this gives me a run-time error '1004', method 'range' of object failed. This confuses me, cuz I've used this method before successfully.
A slight tinker, below, gave me the same issue
LastRow = Cells(Rows.Count, 1).End(xlUp).Row
I've tried adding an additional Dim
Dim PullPlacementname As Range
Set PullPlacementname = ActiveWorkbook.SheetName("Pull").Range("PullPlacementname")
But the second line gives me a run-time error of '438', object doesn't support this property or method.
Thoughts/suggestions?
You could use something like:
Dim c As Long
With Worksheets("Pull")
c = .Range("PullPlacementname").Column ' assuming the range name only contains one column
.Range(.Cells(1, c), .Cells(.Rows.Count, c).End(xlUp)).Copy
End With

Excel VBA - Loop to filter table for each value in column and paste in according worksheet

I am trying to filter a table on the first worksheet ("Data") for each of the items that appear in a table on the second worksheet ("Hosts"), and then paste the filtered results in separate worksheets, each named after the corresponding item on the table.
My understanding of VBA is very basic and I have tried to put together a collage of codes from other users, but it doesn't seem to work properly for me:
The first loop creates worksheets based on the items on the "Hosts" table, but for some reason it adds an extra sheet before the ones I need and calls it "Sheet1"
The second loop simply doesn't work
Are two loops really necessary, or is it possible to combine the two?
This is the code I have so far:
Sub test()
Dim AllData As Worksheet
Dim HostList As Worksheet
Dim DataRange As Range
Dim FilterColumn As Long
Set AllData = ThisWorkbook.Worksheets("Data")
Set HostList = ThisWorkbook.Worksheets("Hosts")
Set DataRange = AllData.Range(Range("A1"), Range("A1").SpecialCells(xlLastCell))
Dim HostValues As Range
For Each HostValues In HostList.ListObjects("Table1").Range
With ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
On Error Resume Next
ActiveSheet.Name = HostValues.Value
If Err.Number = 1004 Then
Debug.Print HostValues.Value & "already used as a sheet name"
End If
On Error GoTo 0
End With
Next HostValues
For Each HostValues In HostList.ListObjects("Table1").Range
AllData.Activate
FilterColumn = 18
DataRange.AutoFilter Field:=FilterColumn, Criteria1:=HostValues
DataRange.SpecialCells(xlCellTypeVisible).Copy
Sheets(HostValues.Text).Range("A1").PasteSpecial xlPasteValues
Selection.Sort Key1:=Range("V:V"), Order1:=xlAscending, Header:=xlGuess
AllData.Activate
Cells.AutoFilter
Next HostValues
End Sub
Some kind soul out there please help me!
There's quite a bit to do here, but I'll give it a shot.
The first loop creates worksheets based on the items on the "Hosts" table, but for some reason it adds an extra sheet before the ones I need and calls it "Sheet1"
My guess here is that Hosts contains a duplicate entry or something is causing the sheet rename section to fail. I would check the debug window for that. Or change
Debug.Print HostValues.Value & "already used as a sheet name"
to
msgBox HostValues.Value & "already used as a sheet name"
That will make a popup happen, should make it easier to see when the error happens. Something else you can try, comment out the two OnError statements with a ' single quote. Then when an error is raised you can hit debug and work through what the program is upset with.
The second loop simply doesn't work
I'm not sure on this one. When you use a For Each many times changing the collection it is operating on will give you some sort of problem. You've asked the computer to do something for every cell in this column, and then you change the values of the column. That's just a guess.
Are two loops really necessary, or is it possible to combine the two?
You can combine the two, after creating the sheet for the Host you can move its data over to it.
Notes
The approach with filtering may be giving you undue complexity try writing a loop without the filters and checking if the Host has a sheet, if it does move the data. If it does not create it and move the data.
You do not need the With/End With block at all.
On Error Resume Next is dangerous. It has its uses, take a look at this for more information or handling errors.
Godspeed.

Resources