interlinking two listbox with commandbutton - vba

I have developed an userform, with two Listbox multiple option.
I have populated the listbox1 with "locations" eg:Germany, USA, UK ; from sheet list.
I have populated listbox2, with the "locations"eg:Germany, USA, UK from sheet list_Man.
My objective is to filter the rows in my sheet "result", depending upon the locations selection. The location are available in column L and M of my sheet "result"
I have an function Do filter for this.
Sub DoFilter()
Dim Ws As Worksheet
Dim strCriteria() As String
Dim arrIdx As Integer
Dim xRow As Integer
arrIdx = 0
For xRow = 2 To Last(1, List.Cells)
If List.Cells(xRow, 2) = True Then
ReDim Preserve strCriteria(0 To arrIdx)
strCriteria(arrIdx) = List.Cells(xRow, 3)
arrIdx = arrIdx + 1
End If
Next xRow
Set Ws = ThisWorkbook.Sheets("Result")
If arrIdx = 0 Then
Ws.UsedRange.AutoFilter
Else
Ws.Range("A:R").AutoFilter Field:=12, Criteria1:=Array(strCriteria), Operator:=xlFilterValues
End If
End Sub
![this is the user form that have been designed. The Listbox1 has the locations from the list sheet and the listbox2 has the locations from the List_Man sheet.
The filter option, according to the selection in the listbox, filters the column L and M of the sheet "Result". exit buton, unloads and returns to the original sheet clearing the filter option.]1
Now the userform works in such a way that, if I select both the listbox, then I get the option filtered.
I would like to have an code in my command button, in such a way that
[![If I am selecting more than one option in listbox1,"USA and UK" and in listbox2, I am selecting "Germany", then I need the filtered rows in result sheet for these option".
also the case in viceversa should be possible. If I select only location2 in listbox2, I should be able to see the filtered rows in the sheet"result".
if I am selecting more than one option in listbox2, and one option in listbox1, then I should be able to see the result accordingly.
I would like to have a code in my command button "Filter" for such condition.
Any lead would be helpful.
this is the code, I am using in my listbox
Private Sub ListBox2_Change()
Dim listboxCounter As Integer
For listboxCounter = 0 To ListBox2.ListCount - 1
If ListBox2.Selected(listboxCounter) = True Then
List_Man.Cells(listboxCounter + 2, 2) = True
Else
List_Man.Cells(listboxCounter + 2, 2) = False
End If
Next
End Sub
this is the code for my userform
Private Sub UserForm_activate()
Dim xRow As Integer
Dim yows As Integer
For xRow = 2 To Last(1, List.Range("A:A"))
With ListBox1
.AddItem List.Cells(xRow, 3).Value
If List.Cells(xRow, 2) = True Then
.Selected(xRow - 2) = True
Else
.Selected(xRow - 2) = False
End If
End With
Next xRow
ListBox1.Height = (xRow - 1) * 15
For yrow = 2 To Last(1, List_Man.Range("A:A"))
With ListBox2
.AddItem List_Man.Cells(yrow, 3).Value
If List_Man.Cells(yrow, 2) = True Then
.Selected(yrow - 2) = True
Else
.Selected(yrow - 2) = False
End If
End With
Next yrow
ListBox2.Height = (xRow - 1) * 15
End Sub

Related

Next buttons userform Excel VBA

I have an admin sheet that has a column containing a list of True and False. I am building a userform UI so users can click next (for now - building previous button after making next work), the userform will show the next False item in admin sheet and its corresponding data in Sheet1 will be displayed in Textbox1.
Reason for this is the row id in admin sheet correlates with Sheet1. So if data in Sheet1 row(31) has something wrong, column(13) in Admin sheet row(31) will be False.
Code:
Dim n As Long
Private Sub CommandButton1_Click()
Dim LR As Long
LR = Sheets("Sheet1").Cells(Rows.count, "B").End(xlUp).row
n = 7
With Worksheets("Admin")
For i = n To LR
If .Cells(i, 13).Value = "False" Then
With Worksheets("Sheet1")
Me.TextBox1 = .Cells(i, 2).Value
Exit For
End With
End If
Next i
End With
n = i + 1
End Sub
This successfully goes to the next False item and displays it correctly in Textbox1. However, it does not iterate to the next one..
Whatever logic we use to set up Next, I am going to assume Previous will be the same?
Thanks guys.
You can do something like this:
Sub cmdNext_Click()
FindRow True
End Sub
Sub cmdPrev_Click()
FindRow False
End Sub
Private Sub FindRow(bForward As Boolean)
Const RW_START As Long = 7
Dim LR As Long, t As Long, dir As Long, i As Long
LR = Sheets("Sheet1").Cells(Rows.Count, "B").End(xlUp).Row
'going forwards, or back?
If bForward Then
n = IIf(n = 0, RW_START, n + 1) '<< Start at top
t = LR '<< towards here
dir = 1 '<< increasing
Else
n = IIf(n = 0, LR, n - 1) '<< Start at bottom
t = RW_START '<< towards here
dir = -1 '<< decreasing
End If
For i = n To t Step dir
If Worksheets("Admin").Cells(i, 13).Value = "False" Then
Me.TextBox1 = Worksheets("Sheet1").Cells(i, 2).Value
n = i
Exit For
End If
Next i
End Sub

Iterations on a userform

I have a userform that I need to get users to enter multiple sets of tasks, as well as an estimate of the time that it will take to undertake each task.
After each task, and time estimate, I would like for the information to be entered on a spreadsheet, and the fields become blank again, for the next task to be entered.
This is the main code:
Global i As Integer ' Rows
Global j As Integer ' Columns
Global tCount As Integer ' Task Count
Sub Time_Calcs()
Dim mcHours As Integer ' M/C process in hours
Dim hDays As Integer ' Hours available per day
i = 2
j = 3
tCount = 1
hDays = 6
Worksheets("Calculations").Activate
Cells.Delete
i = i + 1
Cells(i, 2) = "Item"
Cells(i, 3) = "Task"
Cells(i, 4) = "# of iterations"
Cells(i, 5) = "Maker"
Cells(i, 6) = "Checker"
i = i + 1
TaskForm.Show
End Sub
I have 2 questions:
1) How can I get the code to loop through a series of instructions, such as taking the information from the form, and putting it into a spreadsheet, before clearing the data for the next task to be entered? I've tried this code, but it only seems to work for one iteration.
Private Sub CommandButton1_Click()
j = 3
Cells(i, 2) = tCount
While j <= 6
If j = 3 Then
Cells(i, j) = TaskName
ElseIf j = 4 Then
Cells(i, j) = NoofIts
ElseIf j = 5 Then
Cells(i, j) = mTime
ElseIf j = 6 Then
Cells(i, j) = cTime
End If
j = j + 1
Wend
i = i + 1
j = 3
tCount = tCount + 1
'MSForms.Control(TaskName).Value = vbNullString
'MSForms.Control(NoofIts).Value = vbnullstrins
'MSForms.Control(mTime).Value = vbNullString
'MSForms.Control(cTime).Value = vbNullString
TaskName = vbNullString
NoofIts = vbNullString
mTime = vbNullString
cTime = vbNullString
End Sub
2) After I enter data, I would like the user to be able to TAB to the next box. Currently, if I hit TAB, it TABs the cursor right. How do I get it to enable moving to the next box/button via the TAB button?
You should have a couple of procedures to add the data to the worksheet and to clear the form of existing data.
Clearing the form is just a case of going through each control on the form and settings it's value to some default - usually Null.
Private Sub Reset()
Dim ctrl As Control
For Each ctrl In Me.Controls
Select Case TypeName(ctrl)
Case "TextBox", "ComboBox"
ctrl.Value = Null
Case "OptionButton"
ctrl.Value = False
Case Else
'Do nothing
End Select
Next ctrl
End Sub
Saving the form data can be quite complicated depending on the checks you want to make before allowing the data to be transferred.
I make use of the Tag property of a control to store the column number is should be saved in and the data type of the data.
So something like 16;CCur would indicate it will be saved in column 16 as currency.
The actual code to save the data would start with finding the last cell on the worksheet containing data - this can then be used to place the new data on the next available row.
Each control is then checked and the data saved to the column indicated by in the tag property.
After all the data has been saved the form is reset and the initialize routine executed
Private Sub btnSave_Click()
Dim rLastCell As Range
Dim ctrl As Control
Dim lCol As Long
Dim wrkSht As Worksheet
Set wrkSht = ThisWorkbook.Worksheets("Raw Data")
Set rLastCell = wrkSht.Cells(wrkSht.Rows.Count, 1).End(xlUp).Offset(1)
For Each ctrl In Me.Controls
With ctrl
If TypeName(ctrl) = "TextBox" Or TypeName(ctrl) = "ComboBox" Then
If Trim(ctrl.Value) <> "" Then
If InStr(.Tag, ";") > 0 Then
lCol = Split(.Tag, ";")(0)
'Decide which data type to use.
Select Case Split(.Tag, ";")(1)
Case "CLNG"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CLng(ctrl.Value)
Case "CCur"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CCur(ctrl.Value)
Case "CDATE"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CDate(ctrl.Value)
Case "CSTR", "CSENTENCE"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CStr(ctrl.Value)
Case "CDBL"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CDbl(ctrl.Value)
Case "CPER"
wrkSht.Cells(rLastCell.Row, CLng(lCol)) = CDbl(ctrl.Value) / 100
wrkSht.Cells(rLastCell.Row, CLng(lCol)).NumberFormat = "0.00%"
Case Else
End Select
End If
End If
End If
End With
Next ctrl
Reset
UserForm_Initialize
End Sub
I've added the UserForm_Initlialize procedure as it sets up the form for data entry - todays date is entered in a control, combo-boxes are set up, a label is given a caption showing the current Value Added Tax amount from a named range and the correct control is given focus:
Private Sub UserForm_Initialize()
Me.TextBox1 = Format(Date, "dd-mmm-yyyy")
Me.lblVAT = "VAT # " & Format$(ThisWorkbook.Names("VAT").RefersToRange, "Percent")
With Me.ComboBox1
.AddItem "A"
.AddItem "B"
.AddItem "C"
End With
Me.TextBox1.SetFocus
End Sub
I have extensions to the code - code that automatically converts names to propercase, doesn't allow more than 2 decimal places or only allows whole numbers. There's also code to check that required data has been entered and highlight the controls which are missing data before saving to the sheet. That would take a whole lot more to explain though.

using checkboxes with userform

I have an user form designed with three listboxes.
The 3 listboxes are populated by the location from three different sheets.
By selecting the listbox, the user can filter the data in the sheet "Data".
if the user is selecting the "BBE Bebra" from the Listbox1. then he could find the filtered result of Bebra in the sheet.
Similary, if the user is selecting from the Listbox2, the same procedure is followed and if the user is selecting from listbox3, the same procedure is followed.
The user can also, select all the three checkbox and looks for the filtered result in the sheet.
I have a issues with the working code.
If I am selecting the checkboxes and click "Filter" then I always see the filtered result. The next time I click on the Filter Button I would like to see the whole data sheet with filters clear and checkboxes cleared.
Can someone tell how I can do it ?
Below is the code, I am using in the filter button
Sub DoFilter()
Dim strCriteria() As String
Dim strCriteria2() As String
Dim strcriteria3() As String
Dim arrIdx As Integer
Dim arrIdx2 As Integer
Dim arrIdx3 As Integer
Dim xRow As Integer
Dim arrCounter As Integer
Dim lo As ListObject
arrIdx = 0
arrIdx2 = 0
arrIdx3 = 0
For xRow = 2 To Last(1, List.Cells)
If List.Cells(xRow, 2) = True Then
ReDim Preserve strCriteria(0 To arrIdx)
strCriteria(arrIdx) = List.Cells(xRow, 3)
arrIdx = arrIdx + 1
End If
Next xRow
For xRow = 2 To Last(1, List.Cells)
If List_Man.Cells(xRow, 2) = True Then
ReDim Preserve strCriteria2(0 To arrIdx2)
strCriteria2(arrIdx2) = List_Man.Cells(xRow, 3)
arrIdx2 = arrIdx2 + 1
End If
Next xRow
For xRow = 2 To Last(1, List.Cells)
If List_S.Cells(xRow, 2) = True Then
ReDim Preserve strcriteria3(0 To arrIdx3)
strcriteria3(arrIdx3) = List_S.Cells(xRow, 3)
arrIdx3 = arrIdx3 + 1
End If
Next xRow
Set Ws = ThisWorkbook.Sheets("Data")
Set lo = Ws.ListObjects("Table7")
If arrIdx = 0 And arrIdx2 = 0 And arrIdx3 = 0 Then
'Ws.UsedRange.AutoFilter
Else
With Ws
With lo
'.AutoFilterMode = True
' .UsedRange.AutoFilter
If arrIdx <> 0 Then
.Range.AutoFilter field:=13, Criteria1:=Array(strCriteria), Operator:=xlFilterValues
End If
If arrIdx2 <> 0 Then
.Range.AutoFilter field:=14, Criteria1:=Array(strCriteria2), Operator:=xlFilterValues
End If
If arrIdx3 <> 0 Then
.Range.AutoFilter field:=15, Criteria1:=Array(strcriteria3), Operator:=xlFilterValues
End If
If .AutoFilter.Range.Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count = 1 Then
MsgBox " Your filter has no result"
End If
End With
End With
Dim i As Long
On Error Resume Next
With ThisWorkbook.Worksheets("Dev").PivotTables("PivotTable1").PivotFields("Lo.")
.ClearAllFilters
For i = 1 To .PivotItems.Count
.PivotItems(i).Visible = False
Next
For arrCounter = LBound(strCriteria) To UBound(strCriteria)
.PivotItems(strCriteria(arrCounter)).Visible = True
Next arrCounter
End With
End If
End Sub
I call the function do filter in my button "Filter".
with the button "exit" I always have the
following code
Private Sub CBExit_Click()
If ActiveSheet.FilterMode Then
ActiveSheet.ShowAllData
End If
Sheets("Dev").Select
Sheets("Dev").PivotTables("PivotTable1").PivotFields("Development Loc.").ClearAllFilters
Unload Me
End Sub
You will need to keep track of your current state using some sort of flag. I would do something like the following:
Private Sub Filter_Click()
If Filter.Caption = "Filter" Then
DoFilter
Filter.Caption = "Unfilter"
Else
'do logic to clear
Filter.Caption = "Filter"
End If
End Sub
This has the added benefit of telling the user what the next click of the button will do.

Option Button Existing in two group boxes

I'm trying to create an array of option buttons (or check boxes if you find that works better) where only one option can be selected in each row and column.
I'm trying to do this so that a user can choose an order in which a certain list goes.
The following code takes the column headers in an excel sheet, and creates bunch of option buttons x columns across and x rows down.
Sub Option_Buttons()
Dim str As String
lCol = Sheets("Headers").UsedRange.Columns.Count
Dim OpBtn() As OptionButton
ReDim OpBtn(lCol, lCol) As OptionButton
Dim GrBx() As GroupBox
ReDim GrBx(lCol) As GroupBox
Dim i
Dim j
'
For i = 1 To lCol
Set GrBx(i) = Sheets("OPBTN").GroupBoxes.Add(Cells(i, 1).Left, _
Cells(i, 1).Top, lCol * 50, 17.25)
With GrBx(i)
.Caption = ""
'.Visible = False
End With
For j = 1 To lCol
Set OpBtn(i, j) = Sheets("OPBTN").OptionButtons.Add _
(Cells(i,j).Left, Cells(i, j).Top, 72, 17.25)
str = Sheets("Turf").Cells(1, j)
check = OpBtn(i, j).Value
With OpBtn(i, j)
.Caption = str
.Value = xlOff '
.LinkedCell = "'OData'!" & Sheets("OData").Cells(i, 1).Address
.Display3DShading = False
End With
Next
Next
End Sub
Is there any way I can make it so that only one option can exist in each row AND column? (so if there are 5 column headers, there are 25 option buttons of which only 5 can be selected).
Is there any way to deal with the option that a user doesn't want a header selected?
I'm trying to avoid all possible uses of the controls tab because this is part of a macro that needs to be used by completely excel illiterate people and needs to be applied to highly variable
I was able to accomplish this using checkboxes. The key was to name the checkboxes using R1C1 notation and assigning a macro (ManageCheckBoxes) to all the checkboxes. After identifying what checkbox was clicked using Application.Caller I iterate over all the checkboxes parsing their names to identify which rows and columns they are in.
Sub Option_Buttons()
Dim str As String
Dim r As Integer, c As Integer
Dim check As CheckBox
lCol = Sheets("Headers").UsedRange.Columns.Count
With Sheets("OPBTN")
For r = 1 To lCol
For c = 1 To lCol
Set check = .CheckBoxes.Add(.Cells(r, c).Left, .Cells(r, c).Top, 72, 17.25)
str = Sheets("Turf").Cells(r, c)
With check
.Caption = str
.Value = xlOff '
.Name = "R" & r & "C" & c
.Display3DShading = False
.OnAction = "ManageCheckBoxes"
End With
Next
Next
End With
End Sub
Sub ManageCheckBoxes()
Dim arrCaller As Variant, arrCheck As Variant
Dim check As CheckBox, ckCaller As CheckBox
arrCaller = getRC(Application.Caller)
With Sheets("OPBTN")
Set ckCaller = .CheckBoxes(Application.Caller)
For Each check In .CheckBoxes
If ckCaller.Name <> check.Name Then
If check.Name Like "R#*C#*" Then
arrCheck = getRC(check.Name)
If arrCheck(0) = arrCaller(0) Or arrCheck(1) = arrCaller(1) Then
check.Value = False
End If
End If
End If
Next
End With
End Sub
Function getRC(sName As String)
Dim a(1) As Long
Dim arr As Variant
arr = Split(sName, "C")
a(0) = Right(arr(0), Len(arr(0)) - 1)
a(1) = arr(1)
getRC = a
End Function
use GroupName property of option buttons. GroupName property determines the group of option button. user will be able to select only one option button from the group.
Set same groupname for your five option buttons and same for other five.
For Example :
Option1 GroupName: grp1
Option2 GroupName: grp1
Option3 GroupName: grp2
Option4 GroupName: grp2
in the above case user will be able to select one from option1 and option2. one button from option3 and option4.

Change in adding listbox to sheet from vertical to horizontal and load all results in listbox

After successfully loading my data into a listbox, I was told that the data should be orientated horizontally not vertically. How can implement the new specifications?
Here is the desired result:
Private Sub CommandButton3_Click()
Dim i As Integer
Dim k As Integer
With Me.lsbWarenausgang
For k = 0 To .ListCount - 1
If .Selected(k) = True Then
For i = 1 To 9
Worksheets("Tabelle5").Cells(i, 1) = Me.lsbWarenausgang.List(k, i - 1)
Next i
End If
Next k
End With
End Sub
use a helper array (with same size as listbox columns) to fill with current selected listbox row and then write it down in excel sheet
as for this latter operation use:
.Cells(1, .Columns.Count).End(xlToLeft)
to get the last non empty cell in first row
.Offset(, IIf(IsEmpty(.Cells), 0, 1))
to offset it by one column or not whether the returned range by previous method is a non empty cell or not
.Resize(9) method to reference a 1 column range with same rows as the array
so you can code:
Option Explicit
Private Sub CommandButton3_Click()
Dim i As Long, k As Long
Dim arr(1 To 9) As Variant
With Me.lsbWarenausgang
For k = 0 To .ListCount - 1
If .Selected(k) Then
For i = 1 To 9
arr(i) = .List(k, i - 1)
Next i
With Worksheets("Tabelle5")
With .Cells(1, .Columns.Count).End(xlToLeft)
.Offset(, IIf(IsEmpty(.Cells), 0, 1)).Resize(9) = Application.Transpose(arr)
End With
End With
End If
Next k
End With
End Sub