簡體   English   中英

VB.NET Listview多列過濾器

[英]VB.NET Listview Multiple Column Filter

我的目標是使用多個文本框開發搜索。 我有五個列(ArticleNo,Description,PartNum,Manufacturer和Cost),每個列都有一個文本框。


我使用以下方法跟蹤原始列表項:

Private originalListItems As New List(Of ListViewItem)

這充滿了所有項目(超過6000)。

然后我會根據創建的五個文本框(tbSearchArticleNo,tbSearchDescription,tbSearchPartNum ...等)發生五個“文本更改”事件。

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

這是我的方法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

這是我的方法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

這是我的搜索示例:

tbSearchArticleNo.Text =“33”

這將匹配字符串中包含“33”的每個articleNo。 現在我想添加另一個過濾器:

tbSearchDescription.Text =“混音器”

這應匹配商品編號中包含33的所有內容以及說明中的“調音台”。 依此類推第四名。


實際的過濾器工作正常 - 我唯一的問題是每當我擦除某些東西時,例如“混音器”(雖然文章中仍然有“33”)但它不會返回包含“33”的文章的結果...而是它不會改變我的搜索結果。 可能有更好的搜索方式嗎?

而不是一次只過濾一列並傳遞當前列表以進一步過濾結果,如何讓一個函數一次過濾所有指定的值:

  1. 從每個過濾器文本框的TextChanged(或Validated)事件中調用單個“過濾器”函數。
  2. 在Filter函數中,首先將originalListItem重新復制到lwArticles中。
  3. 為每個具有順序值的文本框應用過濾器。 (為每個文本框調用SearchCurrentList,傳入lwArticles。每個步驟都會從上一個列表中進一步過濾列表,但只對具有值的文本框進行處理。)

很難按照你想要做的,但我建議如果你在listview中處理6k項目並想要過濾它們,也許你應該使用數據綁定gridview代替。

然后,您可以非常簡單地在數據源上執行搜索:

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

處理此問題的一種不同方式是使用LINQ。 以下函數可用於返回枚舉提供的集合的對象,僅包括適合過濾器的項目。 您可以使用此枚舉器重新填充列表。 如果您每次調用GetFilter時都使用了originalListItems,那么您將始終在最新的過濾器中包含所有項目以供考慮。

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

更好的是,稍微考慮一下,你可以將articleNo和其他參數變成具有更大范圍的變量,並調整函數以將IsNullOrEmpty檢查嵌入到Queryable表達式中,然后你甚至不需要重新生成字段值更改時的過濾器。 您可以將變量設置為新的文本框值並重新評估已生成的過濾器表達式,該表達式將考慮變量中的新值,從而產生新的過濾結果。

以下是我希望它的使用方式:

lwArticles.Items.Clear()
For Each i In GetFilter(originalListItems, tbSearchArticleNo.Text, tbSearchDesc.Text, tbPartNum.Text, tbManufacturer.Text, tbCost.Text)
   lwArticles.Items.Add(i)
Next

我設法讓它發揮作用。 對於那些想知道如何,它不是那么優雅 - 但它的工作原理!

我在四個文本框的GUI上設置了標簽(在與工程師進一步交談后,我省略了成本文本框)。 所以..

tbSearchArticleNo.Tag = 1

tbSearchDesc.Tag = 2

tbPartNum.Tag = 4

tbManufacturer.Tag = 8

這並不優雅,因為在添加文本框時,指數會增長。 (注意)根據輸入的字段,計算總數,然后由我的FilterOriginalList(總計)處理

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

這是我的方法:

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

感謝所有幫助過的人。 這是我找到的解決方案 - 我可能會在未來中尋找更容易的東西!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM