1

私の目標は、複数のテキストボックスを使用して検索を開発することです。5つの列(ArticleNo、Description、PartNum、Manufacturer、Cost)があり、それぞれにテキストボックスがあります。


私は以下を使用して元のリストアイテムを追跡します:

Private originalListItems As New List(Of ListViewItem)

これはすべてのアイテム(6000以上)で満たされています。

次に、作成された5つのテキストボックス(tbSearchArticleNo、tbSearchDescription、tbSearchPartNum ...など)に基づいて、5つの「テキスト変更」イベントが発生します。

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が含まれ、説明に「ミキサー」が含まれるすべてのものと一致する必要があります。などなど4番目。


実際のフィルターは正しく機能しています-私の唯一の問題は、「ミキサー」などの何かを消去するたびに(articleNoに「33」が残っている間)、「33」を含むarticleNoの結果が返されないことです...代わりに検索結果は変わりません。これを検索するより良い方法があるかもしれませんか?

4

4 に答える 4

1

これを処理する多少異なる方法は、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
于 2012-12-20T17:18:32.523 に答える
1

一度に 1 つの列だけをフィルター処理して、現在のリストを渡して結果をさらにフィルター処理する代わりに、指定されたすべての値に基づいて一度にフィルター処理する単一の関数を用意するのはどうですか?

  1. すべてのフィルター Textbox の TextChanged (または Validated) イベントから単一の「フィルター」関数を呼び出します。
  2. Filter 関数で、originalListItems を lwArticles に再コピーすることから始めます。
  3. 順番に値を持つ各 Textbox にフィルターを適用します。(テキストボックスごとに SearchCurrentList を呼び出し、lwArticles を渡します。各ステップでは、前のリストからさらにリストを絞り込みますが、値を持つテキストボックスに対してのみ機能します。)
于 2012-12-20T16:48:16.583 に答える
1

あなたがやろうとしていることを理解するのは難しいですが、リストビューで6kのアイテムを扱っていてそれらをフィルタリングしたい場合は、代わりにデータバインドされたグリッドビューを使用することをお勧めします.

次に、データソースで検索を非常に簡単に実行できます。

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
于 2012-12-20T17:08:56.930 に答える
0

私はそれを機能させることができました。どうやって?

4 つのテキスト ボックスの GUI にタグを設定しました (エンジニアとの話し合いの後、コストのテキスト ボックスは省略しました)。そう..

tbSearchArticleNo.Tag = 1

tbSearchDesc.Tag = 2

tbPartNum.Tag = 4

tbManufacturer.Tag = 8

テキストボックスを追加すると指数関数的に大きくなるため、これはエレガントではありません。(注意してください)入力されたフィールドに応じて、合計が計算され、それが私の 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

これが私の方法です:

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

助けてくれたみんなに感謝します。これが私が見つけた解決策です - おそらくもっと簡単なものがあるでしょう。

于 2012-12-20T18:59:21.377 に答える