Excel VBA multiple selection ListBox check if nothing is selected - vba

I am trying to figure out the best way, on how to use Excel ListBox with multiple selections and have a simple VBA code for it to filter multiple sheets based on what is selected in the ListBox.
The code that I have right now is below. At the moment it does pretty much exactly what I need - checks if there is any filter in the sheets, cleans it if so, and then filters out the selected values. But what I need it to do as well, is that there is no value selected at all, it should clean the filters in 4 sheets and exit sub.
The thing is, that I get an "invalid procedure" error if I try to run it when nothing is selected. I did try to add an Else statement and another If to check If .Listindex = -1, but both of the options gave the exact same error.
As this needs to be a multiple selection list, I found that it also needs to loop while checking if nothing is selected, but yet again, had the same error.
How can I improve this code and add the required functionality?
Sub filter1()
Dim MyArray() As String
Dim Cnt As Long
Dim r As Long
Cnt = 0
With Me.ListBox1
If .ListIndex <> -1 Then
For r = 0 To .ListCount - 1
If .Selected(r) Then
Cnt = Cnt + 1
ReDim Preserve MyArray(1 To Cnt)
MyArray(Cnt) = .List(r)
End If
Next r
End If
End With
With Sheet1
If .FilterMode Then .ShowAllData
.Range("A2:Y1037").AutoFilter field:=2, Criteria1:=MyArray, Operator:=xlFilterValues
End With
With Sheet3
If .FilterMode Then .ShowAllData
.Range("A2:AB1037").AutoFilter field:=2, Criteria1:=MyArray, Operator:=xlFilterValues
End With
With Sheet4
If .FilterMode Then .ShowAllData
.Range("A2:Z1037").AutoFilter field:=2, Criteria1:=MyArray, Operator:=xlFilterValues
End With
With Sheet5
If .FilterMode Then .ShowAllData
.Range("A2:Z1037").AutoFilter field:=2, Criteria1:=MyArray, Operator:=xlFilterValues
End With
End Sub

Check if cnt is 0 before the line which says With Sheet1 and if cnt is 0 that means nothing was selected in the ListBox, prompt the user and use exit sub like below...
If cnt = 0 Then
MsgBox "No item was selected in the ListBox." & _
"Please select an item and then try again...", vbCritical, "No Item Selected"
Exit Sub
End If
With Sheet1
End With

Listindex doesnt help when you have multiselect. So instead of using If .ListIndex <> -1 Then checkt your cnt after your loop with
If cnt = 0 'nothing selected
'code for no selection
else
'code with selection
end if

Related

Delete single row above button BUT don't allow it to delete if

I am using form control buttons to add and delete the rows above the button. There are several sections per worksheet, hence the find function.
Adding the row has no issue. The question is about deleting rows... to a limit.
The Code:
Sub Button4_Click()
ActiveSheet.Unprotect
Dim Found As Range
Set Found = Columns("B").EntireRow.Find(what:=" Remove Row ", LookIn:=xlValues, Lookat:=xlWhole)
If Not Found Is Nothing Then
Rows(Found.Row - 1).EntireRow.Delete
End If
ActiveSheet.Protect
End Sub
The problem is that I don't want the end user to delete rows containing headings. I'd like to add something that will stop the sub if the row above the Found.Row contains the text "Travel Expenditures".
It's a "if" I suspect, but I'm stuck.
Any suggestions?
(Novice User)
You were on the right track. A couple comments:
Do you need all the whitespace around "Remove Row"?
Columns("B").EntireRow is the entire sheet - I'm assuming you wanted just Columns("B").
I revised your code based on these 2 issues.
Sub ConditionalDelete()
Dim Found As Range, Found2 As Range
ActiveSheet.Unprotect
Set Found = Columns("B").Find(what:="*Remove Row*", LookIn:=xlValues, Lookat:=xlWhole)
If Not Found Is Nothing Then
Set Found2 = Rows(Found.Row - 1).Find(what:="Travel Expenditures")
If Found2 Is Nothing Then
Rows(Found.Row - 1).Delete
End If
End If
ActiveSheet.Protect
End Sub
you could Intersect rows and column with ActiveSheet.UsedRange to limit scanned ranges
and here's a code with Application.Match method, which I believe is faster than Range.Find(), but is case insensitive:
Sub Button4_Click()
Dim iRow As Variant
iRow = Application.Match(" Remove Row ", Intersect(ActiveSheet.UsedRange, Columns("B")), 0)
If Not IsError(iRow) Then
If IsError(Application.Match("Travel Expenditures", Intersect(ActiveSheet.UsedRange, Rows(iRow - 1).EntireRow), 0)) Then
ActiveSheet.Unprotect
Rows(iRow - 1).Delete
ActiveSheet.Protect
End If
End If
End Sub

Search in Excel loops to infinity, why?

I have a table with two data columns. I need to find the line where both of them are a hit. So far this is what my code looks like:
Dim ws As Worksheet
Set ws = Worksheets(1)
Set rgfound = ws.Range("A:A").Find(MyInputOne.value), LookAt:=xlWhole)
If rgfound Is Nothing Then
MsgBox "No results"
Exit Sub
Else
If rgfound.Offset(0, 3).Value <> MyInputTwo.Value Then
Do
Set rgfound = ws.Range("A:A").FindNext(rgfound)
Loop Until rgfound.Offset(0, 3).Value = MyInputTwo.Value
End If
End If
rgfound.Offset(0, 5).Value = "Found!"
This enters an infinite loop and crashes, and I have to force quit excel after running it.
Any advice will be appreciated.
I wouldn't use loops for this kind of thing.
The built in AutoFilter is super quick if you know how to manipulate it in VBA and it will avoid any need for loops/ problems with infinite loops
Here's the code:
Option Explicit
Sub FindBoth()
Dim sht As Worksheet
Dim data As Range, result As Range
Set sht = ThisWorkbook.Worksheets("Sheet1")
Set data = sht.Range("A1:D101")
sht.AutoFilterMode = False 'clear existing filter
With data
.AutoFilter field:=1, Criteria1:=8
.AutoFilter field:=4, Criteria1:="A"
Set result = .Offset(1, 0).SpecialCells(xlCellTypeVisible).Rows(1)
If Not Intersect(result, data) Is Nothing Then
result.Offset(0, 5).Resize(1, 1).Value = "Found!"
End If
End With
sht.AutoFilterMode = False
End Sub
My data looked like this:
Most probably it loops to infinity, because the code never satisfies this condition:
Loop Until rgfound.Offset(0, 3).Value = MyInputTwo.Value
To see what is happening write:
Debug.Print rgfound.Offset(0, 3).Value
Debug.Print MyInputTwo.Value
before the condition above. Another option to check what is happening is to introduce some counter, which would force the code to stop after the 2000. iteration. Like this:
Public Sub TestMe()
Dim cnt As Long
Do
cnt = cnt + 1
Debug.Assert cnt < 2000
Debug.Print cnt
Loop Until False
End Sub
Once it stops because of the fail of the Debug.Assert condition, you would be able to debug manually and to get what is happening.
Definitely the most simplistic answer here, but if you know your parameters and it should never 'Loop' over a certain amount of times, you can try putting a FOR I / NEXT I statement.
Just another idea to throw out there.

VBA- Moving a row to another sheet and rearrange the order of that they are in the new sheet

I have 4 tabs in my excel workbook: Main Tracking, In Progress, Completed, and Removed. Within the Main Tracking sheet is a list of hundreds of tasks. Columns A through G hold information on each task and column "H" holds a drop down list with the current status of the task. The idea is to be able click on the drop down in Column "H" and adjust the status and if "In Progress" is selected the task in that row will be moved to the "In Progress" tab.
However, the problem I run into is within the "In progress" sheet, I have columns for additional input that were not in the "Main Tracking" sheet. I would also like to retain the "Status" column as the last column to the right in each sheet. In essence I would dropping the data from columns "A:G" into the corresponding "A:G" columns in the "In Progress" tab, but the status column (column "H") would move to the right of my 3 additional columns (so column "K" in this case). Does anyone know a way to do this?
I know this was a long winded question, but any help would be greatly appreciated. Thanks!
Below is my code to move the data to different cells:
Option Explicit
Sub MoveRows()
Application.ScreenUpdating = False
With Worksheets("Main Tracking")
With .Range("H1", .Cells(.Rows.Count, "H").End(xlUp))
FilterAndCopy .Cells, "In Progress"
FilterAndCopy .Cells, "Completed"
FilterAndCopy .Cells, "Remove"
End With
End With
Application.ScreenUpdating = True
End Sub
Sub FilterAndCopy(rng As Range, filterStrng As String)
With rng
.AutoFilter Field:=1, Criteria1:=filterStrng
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then
With .Resize(.Rows.Count).Offset(1).SpecialCells(xlCellTypeVisible).EntireRow
.Copy Destination:=Worksheets(filterStrng).Cells(Rows.Count, "A").End(xlUp).Offset(1)
.Delete
End With
End If
.Parent.AutoFilterMode = False
End With
End Sub
It's tricky working with filtered ranges in-place so easier to move the last column after the paste...
Sub FilterAndCopy(rng As Range, filterStrng As String)
Dim shtDest As Worksheet, rngDest As Range
Set shtDest = Worksheets(filterStrng)
Set rngDest = shtDest.Cells(Rows.Count, "A").End(xlUp).Offset(1)
With rng
.AutoFilter Field:=1, Criteria1:=filterStrng
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then
With .Resize(.Rows.Count).Offset(1).SpecialCells(xlCellTypeVisible).EntireRow
.Copy Destination:=rngDest
.Delete
End With
With shtDest.Range(rngDest, shtDest.Cells(.Rows.Count, 1).End(xlUp)).Offset(0, 7)
.Cut Destination:=.Offset(0, 3)
End With
End If
.Parent.AutoFilterMode = False
End With
End Sub

Creating a multiple field search function in excel VBA

I need to build a linked search function in VBA that also auto-updates after you enter data into the given search fields. I have been able to do this successfully with the following sections of code:
Autofilter search - in a standard module
Code:
Sub FilterTo1Criteria()
With Sheet3
If Range("A3") <> vbNullString Then
.AutoFilterMode = False
.Range("A6:J1015").AutoFilter
.Range("A6:J1015").AutoFilter Field:=1, Criteria1:=Range("A3")
Else
Selection.AutoFilter
End If
End With
End Sub
Sheet change/auto-update - This is in a worksheet module
Code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$3" Then
Application.EnableEvents = False
FilterTo1Criteria
Application.EnableEvents = True
End If
End Sub
However, within the sheet change page, I need cells A3:J3 to be the criteria, but I also need the auto search function to work if only A3 and D3 are filled in, or if just A3 is filled in (D3 is blank), or if just D3 is filled in (A3 is blank), but I'm having issues trying to compound the code to get this effect. How much more complicated will I have to make it? Are there some examples that someone is aware of that I can look at to glean some information from? It's hard to find any...
A slicer with a pivot table is a potential way to go, but I think some people downstream are using Excel 2003 and I don't think the slicer works back that far.
Thanks in advance!
For the function to work if either A3 or D3 are not empty, then you can concatenate the two cells and compare that to vbNullString.
For the multiple filters, you can use a loop to set them all.
eg:
Sub FilterTo1Criteria()
Dim i As Long
With Sheet3
If Range("A3") & Range("D3") <> vbNullString Then
.AutoFilterMode = False
.Range("A6:J1015").AutoFilter
For i = 1 To 10
.Range("A6:J1015").AutoFilter Field:=i, Criteria1:=Cells(3, i)
Next i
Else
Selection.AutoFilter
End If
End With
End Sub
Edit:
It looks like you wanted to set the filters as the criteria cells were filled, rather than all at once. Try this instead:
Sub FilterTo1Criteria()
Dim i As Long
With Sheet3
.AutoFilterMode = False
.Range("A6:J1015").AutoFilter
For i = 1 To 10
If .Cells(3, i) <> vbNullString Then
.Range("A6:J1015").AutoFilter Field:=i, Criteria1:=.Cells(3, i)
End If
Next i
End With
End Sub
and for the new worksheet change sub:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("$A$3:$J$3")) Is Nothing Then
Application.EnableEvents = False
FilterTo1Criteria
Application.EnableEvents = True
End If
End Sub
This will add or remove filters as you add or remove criteria (row 3).

Selecting/deleting certain rows depending on value

I wrote this script to delete rows which contain a value in column C that is different than "201103". When I use this to bold it, it works, but when I use it with .Delete it behaves strange and does not work properly.
I was trying to get selected rows and than use UNION to merge it and use .SELECT (multiple) so I could delete it manually but not sure how to make it.
Sub test()
Dim Cell As Range
For Each Cell In Range("C2:C2308").Cells
If (Cell.Value <> "201103" And Cell.Value <> "") Then
Cell.EntireRow.Font.Bold = True
'Cell.EntireRow.Delete
End If
Next Cell
End Sub
Does anyone know how to fix it so it works fine?
Try this:
Sub test()
'
With ActiveSheet
.AutoFilterMode = False
With Range("C2", Range("C" & Rows.Count).End(xlUp))
.AutoFilter 1, "<>201103"
On Error Resume Next
.Offset(1).SpecialCells(12).EntireRow.Delete
End With
.AutoFilterMode = False
End With
End Sub