My goal is to develop a search using multiple textboxes. I have five columns (ArticleNo, Description, PartNum, Manufacturer and Cost) each of which will have a textbox over them.
I keep track of the original list items using:
Private originalListItems As New List(Of ListViewItem)
This is filled with all the items (over 6000).
Then I will have five "text changed" events occuring based on the five textboxes created (tbSearchArticleNo, tbSearchDescription, tbSearchPartNum ... etc)
Private Sub tbSearchArticleNo_TextChanged(sender As Object, e As System.EventArgs) Handles tbSearchArticleNo.TextChanged
If tbSearchDesc.Text <> "" Or tbPartNum.Text <> "" Or tbManufacturer.Text <> "" Or tbCost.Text <> "" Then
SearchCurrentList(lwArticles, tbSearchArticleNo.Text, 0, False)
Else
SearchListView(lwArticles, tbSearchArticleNo.Text, 0, False)
End If
End Sub
Here is my method SearchCurrentList:
Private Sub SearchCurrentList(ByVal listview As ListView, ByVal search As String, ByVal colIndex As Integer, ByVal upperCase As Boolean)
If upperCase Then
search = search.ToUpper()
End If
listview.BeginUpdate()
'Clear listview
lwArticles.Items.Clear()
'Other textbox has information in it, concatenate both results
For Each item In currentListItems
Dim itemToUpper = item.SubItems.Item(colIndex).Text
If upperCase Then
itemToUpper = item.SubItems.Item(colIndex).Text.ToUpper()
End If
If itemToUpper.Contains(search) Then
lwArticles.Items.Add(item)
End If
Next
'Reupdate the current list of items
currentListItems.Clear()
For Each item In lwArticles.Items
currentListItems.Add(item)
Next
listview.EndUpdate()
End Sub
And here is my method SearchListView:
Private Sub SearchListView(ByVal listview As ListView, ByVal search As String, ByVal colIndex As Integer, ByVal upperCase As Boolean)
'Upper case parameter determines if you're searching a string, if so, it is better to compare everything by uppercase
If upperCase Then
search = search.ToUpper()
End If
listview.BeginUpdate()
If search.Trim().Length = 0 Then
'Clear listview
listview.Items.Clear()
'Clear currentListItems
currentListItems.Clear()
'If nothing is in the textbox make all items appear
For Each item In originalListItems
listview.Items.Add(item)
Next
Else
'Clear listview
listview.Items.Clear()
'Clear currentListItems
currentListItems.Clear()
'Go through each item in the original list and only add the ones which contain the search text
For Each item In originalListItems
Dim currItem = item.SubItems.Item(colIndex).Text
If upperCase Then
currItem = currItem.ToUpper()
End If
If currItem.Contains(search) Then
currentListItems.Add(item)
listview.Items.Add(item)
End If
Next
End If
listview.EndUpdate()
End Sub
Here's an example of my search:
tbSearchArticleNo.Text = "33"
This will match every articleNo that contains "33" in the string. Now I want to add another filter:
tbSearchDescription.Text = "Mixer"
This should match everything that contains 33 in the article number as well as "mixer" in the description. And so on and so fourth.
The actual filters are working correctly - my only problem is whenever I erase something, such as "Mixer" (while still having "33" in the articleNo) it doesn't return the results of the articleNo containing "33" ... Instead it doesn't change the results of my search. There might be a better way of searching through this?
Instead of only filtering one column at a time and passing the current list in to further filter the results, how about having a single function that filters based on all the specified values at once:
Its hard to follow what you are trying to do, but i suggest if you are dealing with 6k items in a listview and want to filter them, perhaps you should use a databound gridview instead.
Then you can perform the search on the datasource, very simply:
Public Class Form1
Private _articleList As List(Of Article)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'populate _itemList somehow, for example from a database. Manually here for example purposes:
_articleList = New List(Of Article) From {
New Article("jenny cooks fish", "cooking"),
New Article("a better sales team", "sales")}
DataGridView1.DataSource = _articleList
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim filtered As List(Of Article) = _articleList.Where(Function(x) x.Title.Contains("cook") AndAlso x.Category = "cooking").ToList
DataGridView1.DataSource = filtered
End Sub
End Class
Public Class Article
Property Title As String
Property Category As String
'etc etc
Public Sub New(ByVal title As String, ByVal category As String)
_Title = title
_Category = category
End Sub
End Class
A somewhat different way of handling this is to use LINQ. The following function could be used to return an object that enumerates the provided collection including only those items that fit the filter. You could use this enumerator to re-populate your list. If you used originalListItems each time you called GetFilter, you would always have every item included for consideration in the latest filter.
Function GetFilter(source As IEnumerable(Of ListViewItem), articleNo As String, description As String,
partNum As String, prop4 As String, prop5 As String) As IQueryable(Of ListViewItem)
GetFilter = source.AsQueryable
Dim articleFilter As Expressions.Expression(Of Func(Of ListViewItem, Boolean)) = _
Function(i As ListViewItem) i.SubItems(0).Text.IndexOf(articleNo, StringComparison.InvariantCultureIgnoreCase) >= 0
Dim descFilter As Expressions.Expression(Of Func(Of ListViewItem, Boolean)) = _
Function(i As ListViewItem) i.SubItems(1).Text.IndexOf(description, StringComparison.InvariantCultureIgnoreCase) >= 0
Dim partFilter As Expressions.Expression(Of Func(Of ListViewItem, Boolean)) = _
Function(i As ListViewItem) i.SubItems(2).Text.IndexOf(partNum, StringComparison.InvariantCultureIgnoreCase) >= 0
Dim prop4Filter As Expressions.Expression(Of Func(Of ListViewItem, Boolean)) = _
Function(i As ListViewItem) i.SubItems(3).Text.IndexOf(prop4, StringComparison.InvariantCultureIgnoreCase) >= 0
Dim prop5Filter As Expressions.Expression(Of Func(Of ListViewItem, Boolean)) = _
Function(i As ListViewItem) i.SubItems(4).Text.IndexOf(prop5, StringComparison.InvariantCultureIgnoreCase) >= 0
If Not String.IsNullOrEmpty(articleNo) Then GetFilter = Queryable.Where(GetFilter, articleFilter)
If Not String.IsNullOrEmpty(description) Then GetFilter = Queryable.Where(GetFilter, descFilter)
If Not String.IsNullOrEmpty(partNum) Then GetFilter = Queryable.Where(GetFilter, partFilter)
If Not String.IsNullOrEmpty(prop4) Then GetFilter = Queryable.Where(GetFilter, prop4Filter)
If Not String.IsNullOrEmpty(prop5) Then GetFilter = Queryable.Where(GetFilter, prop5Filter)
End Function
Better yet, with a little more thought, you could probably make articleNo and the other parameters into variables with a larger scope, and adjust the function to embed IsNullOrEmpty checking into the Queryable expression, and then you wouldn't need to even re-generate the filter when a field value changes. You could just set the variable to the new textbox value and re-evaluate the already-generated filter expression, which will consider the new values in the variables thus yielding newly filtered results.
Here's how I would expect it to be used:
lwArticles.Items.Clear()
For Each i In GetFilter(originalListItems, tbSearchArticleNo.Text, tbSearchDesc.Text, tbPartNum.Text, tbManufacturer.Text, tbCost.Text)
lwArticles.Items.Add(i)
Next
I managed to get it to work. For those wondering how, it's not that elegant - but it works!
I set tags on the GUI of the four textboxes (i omitted the cost textbox after further talk with the engineers). So..
tbSearchArticleNo.Tag = 1
tbSearchDesc.Tag = 2
tbPartNum.Tag = 4
tbManufacturer.Tag = 8
This isn't elegant because you will exponentially grow as you add textboxes. (be careful) Depending on the fields entered, a total is calculated which will then be treated by my FilterOriginalList(total)
Private Sub tbSearchArticleNo_TextChanged(sender As Object, e As System.EventArgs) Handles tbSearchArticleNo.TextChanged, tbSearchDesc.TextChanged, tbPartNum.TextChanged, tbManufacturer.TextChanged
Dim tag1 As Integer = 0
Dim tag2 As Integer = 0
Dim tag3 As Integer = 0
Dim tag4 As Integer = 0
If tbSearchArticleNo.Text <> "" Then
tag1 = tbSearchArticleNo.Tag
End If
If tbSearchDesc.Text <> "" Then
tag2 = tbSearchDesc.Tag
End If
If tbPartNum.Text <> "" Then
tag3 = tbPartNum.Tag
End If
If tbManufacturer.Text <> "" Then
tag4 = tbManufacturer.Tag
End If
FilterOriginalList(tag1 + tag2 + tag3 + tag4)
End Sub
Here is my method:
Private Sub FilterOriginalList(ByRef tagCounter As Integer)
Dim field1 As String = tbSearchArticleNo.Text
Dim field2 As String = tbSearchDesc.Text.ToUpper()
Dim field4 As String = tbPartNum.Text.ToUpper()
Dim field8 As String = tbManufacturer.Text.ToUpper()
lwArticles.BeginUpdate()
'Clear listview
lwArticles.Items.Clear()
For Each item In originalListItems
Dim currField1 = item.SubItems.Item(0).Text
Dim currField2 = item.SubItems.Item(1).Text.ToUpper
Dim currField4 = item.SubItems.Item(2).Text.ToUpper
Dim currField8 = item.SubItems.Item(3).Text.ToUpper
Select Case (tagCounter)
Case 0
lwArticles.Items.Add(item)
Case 1
If currField1.Contains(field1) Then
lwArticles.Items.Add(item)
End If
Case 2
If currField2.Contains(field2) Then
lwArticles.Items.Add(item)
End If
Case 3
If currField1.Contains(field1) And currField2.Contains(field2) Then
lwArticles.Items.Add(item)
End If
Case 4
If currField4.Contains(field4) Then
lwArticles.Items.Add(item)
End If
Case 5
If currField1.Contains(field1) And currField4.Contains(field4) Then
lwArticles.Items.Add(item)
End If
Case 6
If currField2.Contains(field2) And currField4.Contains(field4) Then
lwArticles.Items.Add(item)
End If
Case 7
If currField1.Contains(field1) And currField2.Contains(field2) And currField4.Contains(field4) Then
lwArticles.Items.Add(item)
End If
Case 8
If currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 9
If currField1.Contains(field1) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 10
If currField2.Contains(field2) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 11
If currField1.Contains(field1) And currField2.Contains(field2) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 12
If currField4.Contains(field4) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 13
If currField1.Contains(field1) And currField4.Contains(field4) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 14
If currField2.Contains(field2) And currField4.Contains(field4) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
Case 15
If currField1.Contains(field1) And currField2.Contains(field2) And currField4.Contains(field4) And currField8.Contains(field8) Then
lwArticles.Items.Add(item)
End If
End Select
Next
lwArticles.EndUpdate()
End Sub
Thanks for everyone who helped. Here's the solution I found - there's probably something much easier that I will be looking into in the futur!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.