Extract data from a two columned listbox to a sheet - vba

I have a two-columned listbox, which I've manually added entries to using
.AddItem (potato)
.List(.ListCount - 1, 1) = bananaTbx.Text
When the user closes the userform all of the data is lost, so I want to have a save & exit button which saves the data to a sheet. However, it can't be saved to specific cells as the size of the list is dynamic and they will continually be adding to the master list in the sheet.
I tried to do something like this to extract the data:
Dim i As Integer
'loop through each row number in the list
For i = 0 To Userform1.Listbox1.ListCount - 1
'create sequence 1,1,2,2,3,3,4,4 ... to reference the current list row
j = Application.WorksheetFunction.RoundDown(i + 0.5, 0)
'create sequence 0,1,0,1,0,1,0,1 ... to reference current column in list
If Len(CStr(i / 2)) > 1 Then
k = 0
Else
k = 1
Sheets("Data").Range("A1" & ":" & "A" & i).Value = Userform1.ListBox1.List(j, k)
End If
Error:
1004 Object defined error
How can I do this properly or in a more efficient manner?

I have created a simple userform to demonstrate how to Extract Values / Data from a multi-column Listbox on a Userform
Start by creating a simple userform with a few controls
add a new Module1 to your Project and stick the below code in it
Sub TestUserForm()
UserForm1.Show
Unload UserForm1
End Sub
in Project Explorer (VBE) Right-click on the UserForm1 and hit View Code
Copy and paste the below code
Private Sub CommandButton1_Click()
With ListBox1
.AddItem TextBox1.Value
.List(.ListCount - 1, 1) = TextBox2.Value
End With
End Sub
Private Sub CommandButton2_Click()
Dim ws As Worksheet
' create a results sheets if you do not already have one
Set ws = Sheets("Results")
Dim nextAvailableRow As Long
Dim i As Long
For i = 0 To ListBox1.ListCount - 1
nextAvailableRow = ws.Range("A" & Rows.Count).End(xlUp).Row + 1
ws.Range("A" & nextAvailableRow) = ListBox1.Column(0, i)
ws.Range("B" & nextAvailableRow) = ListBox1.Column(1, i)
Next i
Me.Hide
End Sub
Create a new spreadsheet and name it Results
Run the TestUserForm macro
Add sample data to the list and click Save button

Related

vba for Linking the Listboxes in userform and accessing through button

I have created an userform with two Listboxes having Multiple option.
The Listbox1, is populated with the Location 1 of column L of sheet Result.
The Listbox2, is populated with the Location 2 of column M of sheet Result.
I have an Button designed as "GO!!"
Right now, I have a code, that works in such a way that, you click on the Listbox 1 (eg: Germany is Location1 , it filters for location )
Similarly, If you chose Listbox2 (eg.USA in location 2, it filters for location)
I would like to have a code, such a way that the two listboxes should be interlinked and accessed through button.
For Eg. If I click "USA" from Listbox1. "Germany" from Listbox2, I Click "GO", It should show me the results of USA;with Germany in my sheet2.
Also,If I am not selecting the Listbox2, then it should display the result only for listbox1 and viceversa.
Here is the code, I am using for listbox1.
I change the Listbox name for second listbox and the column number with the same code.
Private Sub DoFilter1()
Dim ws As Worksheet
Dim strCriteria() As String, i As Integer, arrIdx As Integer
ReDim Preserve strCriteria(0 To arrIdx)
arrIdx = 0
For i = 0 To Me.ListBox1.ListCount - 1
If Me.ListBox1.Selected(i) Then
ReDim Preserve strCriteria(0 To arrIdx)
strCriteria(arrIdx) = Me.ListBox1.List(i)
arrIdx = arrIdx + 1
End If
Next i
Set ws = Sheets("Result")
If arrIdx = 0 Then
ws.UsedRange.AutoFilter
Else
ws.Range("A:P").AutoFilter Field:=12, Criteria1:=Array(strCriteria), Operator:=xlFilterValues
End If
End Sub
Could some one suggest me , how I could interlink them and access through buttons. Any lead would be helpful.
This is how my userform looks like. I would like to select the checkbox from the list and then click Ok to show the result. Also, I should be able to select more than one option in checkboxes.
Please try the below code, This might be helpful. Thanks
With ListBox1
For x = 0 To .ListCount - 1
If .Selected(x) Then
temp = temp & Chr(10) & .List(x)
End If
Next
End With
With ListBox2
For x = 0 To .ListCount - 1
If .Selected(x) Then
temp = temp & Chr(10) & .List(x)
End If
Next
End With
MsgBox temp & " is selected"

Dictionary doesn't display Items for certain Key (Numeric value)

This is a Long one, but stay with me...
I have a Dictionary that saves "PO" as Key and "SO" as Items (there can be cases that a certain "PO" has multiple "SO".
My Excel data in a worksheet, where the Dictionary get's his values looks like this:
The code for populating the Dictionary (working) looks like this:
Option Explicit
Const OrdersDBShtName As String = "Orders_DB"
Public OrdersDBSht As Worksheet
Public LastSORow As Long
Public PODict As Object ' Public Dictionay for PO#, and keep SO per PO# (unique ID)
'======================================================================
Sub InitDict()
Dim AdminSht As Worksheet
Dim i As Long, j As Long
' set the sheet object where the "Orders_DB" data lies
On Error Resume Next
Set OrdersDBSht = ThisWorkbook.Worksheets(OrdersDBShtName)
On Error GoTo 0
If OrdersDBSht Is Nothing Then ' in case someone renamed the "Admin" Sheet
MsgBox Chr(34) & OrdersDBShtName & Chr(34) & " Sheet has been renamed, please modify it", vbCritical
End
End If
With OrdersDBSht
LastSORow = .Cells(.Rows.Count, "B").End(xlUp).Row ' get last row with data in column "B" ("SO#")
' get all SO numbers in Dictionary (SO# is unique ID)
Set SODict = CreateObject("Scripting.Dictionary")
' get all PO's in Dictionary (PO# is unique, but there can be several SO# per PO#)
Set PODict = CreateObject("Scripting.Dictionary")
Dim ID As Variant, Names As String
' add unique Category values to Dictionary object , and save Item Names in Names
For i = 2 To LastSORow
If Not PODict.Exists(.Range("A" & i).Value) Then
ID = .Range("A" & i).Value
For j = 2 To LastSORow
If .Range("A" & j).Value = ID Then
If Names = "" Then
Names = .Range("B" & j).Value ' get the SO#
Else
Names = Names & "," & .Range("B" & j).Value ' get the SO#
End If
End If
Next j
PODict.Add ID, Names
End If
ID = Empty
Names = Empty
Next i
' section below for DEBUG Only (works)
Dim Key As Variant
For Each Key In PODict.keys
Debug.Print Key & " | " & PODict(Key)
Next Key
End With
End Sub
The Problem: I have a User_Form with 2 ListBoxes.
ExistingPO_LB - a ListBox for "PO"s, reads all the Unique Keys in the Dictionary object.
ExistingSO_LB - a ListBox for "SO#", should show only Items for the Key selected in ExistingPO_LB.
In some cases (like the screen-shot below) it works:
In some cases (like the screen-shot below) it doesn't (even though the Items have been saved correctly in PODict Dictionary object):
User_Form Code
Private Sub EditSO_Btn_Click()
With Me.ExistingSO_LB
For i = 0 To .ListCount - 1
If .Selected(i) Then
EditSONumer = .List(i)
Exit For
End If
Next i
End With
If EditSONumer = 0 Then
MsgBox "No SO was selected from the list", vbInformation
Exit Sub
End If
Unload Me
AddEdit_Orders_Form.Show ' call sub Edit Order (load Add Order with the SO# data requested)
End Sub
'=========================================================
Private Sub ExistingPO_LB_Click()
' ****** This is the Sub I guess I'm missing something ******
Dim i As Long
Dim POSelected As Variant
Dim SOArr As Variant
With Me.ExistingPO_LB
For i = 0 To .ListCount - 1
If .Selected(i) Then
POSelected = .List(i)
Exit For
End If
Next i
End With
' update the SO listbox with only relevant SO (from the selected PO)
SOArr = Split(PODict(POSelected), ",") '<=== PODict(POSelected) return empty ???
With Me.ExistingSO_LB
.Clear ' clear the previous items
For i = LBound(SOArr) To UBound(SOArr)
.AddItem SOArr(i)
Next i
End With
End Sub
'=========================================================
Private Sub UserForm_Initialize()
' load all existing PO's from "Orders DB" sheet
Dim Key As Variant
' populate listbox with PO's
With Me.ExistingPO_LB
For Each Key In PODict.keys
.AddItem Key
Next Key
End With
End Sub
The numeric keys were entered as numbers and you are fetching them as strings. I suggest that you stick to one convention for your dictionary.
Sub TestDict()
Dim dict As New Dictionary
dict.Add 1, "one"
dict.Add "2", "two"
Debug.Print dict("1") ' Nothing
Debug.Print dict(1) ' one
Debug.Print dict("2") ' two
Debug.Print dict(2) ' Nothing
End Sub
Solution
Chose a convention for your dictionary and stick to it. In this application I would take the convention of always converting my keys to strings, both when inserting and when fetching. A few changes in your code can achieve it:
If Not PODict.Exists(CStr(Range("A" & i).Value) Then ' could use .Text also
PODict.Add CStr(ID), Names
SOArr = Split(PODict(CStr(POSelected)), ",") ' maybe not needed here, but to illustrate

Macro Button to transfer info from certain cells in one spreadsheet into a different spreadsheet

I would like to be able to construct a macro that is easily able to transfer cell content from one spreadsheet to another. Let me elaborate in more detail: I currently have two spreadsheets open. See picture. The worksheet on the left works via a button macro that I made (not included in picture) and generates three different adjacent values. Thus every time I would click the button, a new output would be generated.
What I would like to be able to do is to transfer that information from the worksheet on the left to the worksheet on the right into columns G, H, and I respectively (by potentially clicking the button on the right worksheet) and then having it go to the next blank row to prepare for next round of generated values.
I'm having a bit of trouble constructing this (beginner). Could you offer some assistance?
Here is what I have so far:
Sub Button1_Click()
If Not Intersect(ActiveCell, Range("G:G")) Is Nothing Then
If ActiveCell.Offset(1, 0) <> vbNullString Then
ActiveCell.Offset(1, 0).Select
ActiveCell.Value = Workbooks("Model.xlsx").Worksheets("Optimization").Range("C100").Value
ActiveCell.Offset(0, 1).Value = Workbooks("Model.xlsx").Worksheets("Optimization").Range("D100").Value
ActiveCell.Offset(0, 2).Value = Workbooks("Model.xlsx").Worksheets("Optimization").Range("E100").Value
End If
End If
End Sub
Thanks!
This should work
Sub Button1_Click()
Const RowToTakeInformation = 100
Const ColumnToTakeInformation = 3
Const ColToWriteIn = 7
Dim WBDesired As Workbook: Set WBDesired = Workbooks("Model.xlsx")
Dim WSDesired As Worksheet: Set WSDesired = Worksheets("Optimization")
'If everything is in the same WB, I don't see a valid reason why to set it
Dim RowToInsert As Long
Dim CounterColumnsToWrite As Long
If WBDesired.Worksheets(WSDesired.Name).Cells(RowToTakeInformation, ColumnToTakeInformation).Value <> "" Then ' 1. If WBDesired.Worksheets(WSDesired.Name).Cells(RowToTakeInformation, ColumnToTakeInformation).Value <> ""
'here I'm assuming it's a table or something like it that automatically recalculates when something is inserted in between
RowToInsert = Cells(Rows.Count, ColToWriteIn).End(xlUp).Row - 1
Rows(RowToInsert).Insert Shift:=xlDown
For CounterColumnsToWrite = 0 To 2
Cells(RowToInsert, ColToWriteIn + CounterColumnsToWrite).Value = WBDesired.Worksheets(WSDesired.Name).Cells(RowToTakeInformation, ColumnToTakeInformation + CounterColumnsToWrite)
Next CounterColumnsToWrite
End If ' 1. If WBDesired.Worksheets(WSDesired.Name).Cells(RowToTakeInformation, ColumnToTakeInformation).Value <> ""
End Sub

Excel to copy matching cell row from tabs to a summary tab in the same workbook

I have a workbook and I need to find the NO values on ROW G (Row 7) and then copy the line that NO belongs to a new sheet (TAB) called summary, in my case it is listed as sheet 18.
I need to search on all sheets though from Sheet 1 to Sheet 17 in their G Rows for NO's.
I have a code I have found online and amend it to work with my criteria. But it does not seem to work as I would like it to it keeps coming up with errors.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim nxtRow As Integer`enter code here`
'Determine if change was to Column G (7)
If Target.Column = 7 Then
'If Yes, Determine if cell = NO
If Target.Value = "NO" Then
'If Yes, find next empty row in Sheet 18
nxtRow = Sheets(18).Range("F" & Rows.Count).End(xlUp).Row + 1
'Copy changed row and paste into Sheet 18
Target.EntireRow.Copy _
Destination:=Sheets(18).Range("A" & nxtRow)
End If
End If
End Sub
Thank you in advance.
Vasilis.
http://s38.photobucket.com/user/Greekcougar/media/screenshot9_zpslhtkkfue.jpg.html
http://s38.photobucket.com/user/Greekcougar/media/sub%20macro_zpsngyjtsj9.jpg.html
Below is the code for the same. It has two sub procedures initiate and applyFilterAndCopy. In initiate you can specify the no. of sheets(sheetCount In below code I have mentioned as 2) you need to scan. While calling the second sub procedure inside first(initiate) you need to specify the column number and the text you are searching for as variables to the second sub procedure(Call applyFilterAndCopy(i, 1, "No") here I have mentioned as 1 i.e. 1st column and String to be searched as No in quotes). Please note the sheet names need to be in the format Sheet**** and summary sheet name as Summary case sensitive as mentioned in your description.
Sub initiate()
Worksheets("Summary").UsedRange.Delete
Dim i As Integer, sheetCount As Integer
sheetCount = 2
For i = 1 To sheetCount
Call applyFilterAndCopy(i, 1, "No")
Next i
End Sub
Sub applyFilterAndCopy(sheetNo As Integer, searchInColumn As Integer, textToSearch As String)
Worksheets("Sheet" & sheetNo).AutoFilterMode = False
Worksheets("Sheet" & sheetNo).Range("A1").AutoFilter Field:=searchInColumn, Criteria1:=textToSearch
DR = Worksheets("Summary").UsedRange.SpecialCells(xlCellTypeLastCell).Row
If IsEmpty(DR) = True Or DR = 1 Then
Worksheets("Sheet" & sheetNo).UsedRange.SpecialCells(xlCellTypeVisible).Copy _
Destination:=Worksheets("Summary").Range("A1")
Else
Worksheets("Sheet" & sheetNo).UsedRange.Offset(1, 0).SpecialCells(xlCellTypeVisible).Copy _
Destination:=Worksheets("Summary").Range("A" & DR + 1)
End If
End Sub
Vba is not necessary for this. An easy way to do this is using a formula and filter.
Add a column to the sheet that looks at the previous row and checks if no is there. Then filter this column and you can then just copy and paste to your summary tab.

Quicker way to filter out data based on a particular value

I am working with a workbook that currently has 3 sheets. The first sheet is an overview where the filtered data will appear. Cell D11 has the color that I am looking for. Upon entering the color cells F3:I27 Populate with information like color, shape, number and animal.
C2C-Tracker2
I would use a Pivot Table for this, however, I have another set of data in K3:M27. This data is pulled from another sheet within the workbook with a similar function.
The formula that I am using is:
=IFERROR(INDEX(cases!A:A,SMALL(IF(EXACT($D$3,cases!$C:$C),ROW(cases!$C:$C)-ROW($F$1)+1),ROW(1:1))),"")
Of course it is entered using CTRL + SHIFT + ENTER for it to work properly.
I tried using a VBA Macro that I pulled from the video below:
Excel VBA Loop to Find Records Matching Search Criteria
So many array formulas can really make your workbook very slow.
Here is a code to populate Dataset1 using arrays. It runs in less than a second.
Hope this gets you started. I have commented the code but if you still have a problem understanding, just post back :)
Sub Sample()
Dim DSOne() As String
Dim tmpAr As Variant
Dim wsCas As Worksheet: Set wsCas = ThisWorkbook.Sheets("Cases")
Dim wsMain As Worksheet: Set wsMain = ThisWorkbook.Sheets("Sheet1")
Dim lRow As Long, i As Long, j As Long
'~~> Check if user entered a color
If wsMain.Range("D3").Value = "" Then
MsgBox "Please enter a color first", vbCritical, "Missing Color"
Exit Sub
End If
'~~> Clear data for input in main sheet
wsMain.Range("F3:F" & wsMain.Rows.Count).ClearContents
'~~> Get last row of Sheet Cases
lRow = wsCas.Range("A" & wsCas.Rows.Count).End(xlUp).Row
With wsCas
'~~> Get count of cells which have that color
i = Application.WorksheetFunction.CountIf(.Columns(3), wsMain.Range("D3").Value)
'~~> Check if there is any color
If i > 0 Then
'~~> Define your array to hold those values
ReDim DSOne(1 To i, 1 To 4)
'~~> Store the Sheet Cases data in the array
tmpAr = .Range("A1:D" & lRow).Value
j = 1
'~~> Loop through the array to find the matches
For i = LBound(tmpAr) To UBound(tmpAr)
If tmpAr(i, 3) = wsMain.Range("D3").Value Then
DSOne(j, 1) = tmpAr(i, 1)
DSOne(j, 2) = tmpAr(i, 2)
DSOne(j, 3) = tmpAr(i, 3)
DSOne(j, 4) = tmpAr(i, 4)
j = j + 1
End If
Next i
'~~> write to the main sheet in 1 Go!
wsMain.Range("F3").Resize(UBound(DSOne), 4).Value = DSOne
End If
End With
End Sub
Screenshot:
Using the above approach now populate Dataset2 :)