vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: .NET   |   VB-Versionen: VB2005, VB200804.05.09
ListView um einen Suchdialog erweitern

In vielen Anwendungen ist es selbstverständlich, das man in einem Inhalt suchen kann. Das ListView-Control bietet jedoch leider keine Suche, lässt sich aber mit folgendem Tipp nachrüsten.

Autor:  Carsten StuplichBewertung:  Views:  13.970 

Der hier Erstelle Suchdialog berücksichtigt dabei, ob in dem ListView-Control ein Multi-Row-Select möglich ist oder nicht.

Als erstes erstellen wir ein Formular mit dem Namen frmFindInListView, das folgende Controls enthält:

ControlName
LabelLabel1
LabelLabel2
TextBoxtxtSearchText
ComboBoxcombSearchColumn
CheckBoxchkNonCaseSensetive
CheckBoxchkOnlyWholeWord
CheckBoxchkSearchTextIsRegEx
ButtonOK_Button
ButtonCancel_Button

Der Dialog sieht dann wie folgt aus:

In das Formular kopiert man nun folgenden Sourcecode:

Imports System.Windows.Forms
Imports System.Text.RegularExpressions
 
Public Class frmFindInListView
 
  Private objListview As ListView
  Private objMainForm As Form
  Private bolIngnoreColumnsWithZeroWidth As Boolean
 
  ' Wird benötigt umd bei einfach Auswhal festzusellen, das schon mal gesucht wurde
  Private bSearched As Boolean = False
  Private iLastFoundRowIndex As Integer = 0, iLastCheckedRowIndex As Integer
  Private bTextFound As Boolean = False
  Private Sub OK_Button_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles OK_Button.Click
 
    ' Alle Texte in LisView selektieren, in dem der Text vorkommt
    Dim i As Integer, y As Integer, iSearchStart As Integer
    Dim bShowMsg As Boolean = True
    Dim bExit As Boolean = False
 
    ' Wenn bei letzer Suche (Single-Select in ListView) die letzte 
    ' Zeile erreicht wurde, dann Suche von neu beginnen
    If objListview.MultiSelect = False AndAlso _
      iLastCheckedRowIndex = objListview.Items.Count - 1 Then
 
      bSearched = False
      iLastFoundRowIndex = 0
      iLastCheckedRowIndex = 0
    End If
 
    ' Startzeile für Suche festlegen (jenachdem ob Liste Multi-Select erlaub oder nicht)
    If objListview.MultiSelect = True Then
      iSearchStart = 0
 
      ' alle Selektionen bei Multi-Select entfernen
      Dim oListViewItem As ListViewItem
      For Each oListViewItem In objListview.SelectedItems
        oListViewItem.Selected = False
      Next 
    Else
      If bSearched = False Then
        ' Bei ersten Mal in erster Zeilen beginnen
        iSearchStart = 0
      Else
        iSearchStart = iLastFoundRowIndex + 1
      End If
    End If
 
    bSearched = True
    If combSearchColumn.SelectedIndex <> combSearchColumn.Items.Count - 1 Then
      ' Suche über eine Spalte
      For i = iSearchStart To objListview.Items.Count - 1
        iLastCheckedRowIndex = i
        Try
          If ContainsPlus(objListview.Items(i).SubItems(combSearchColumn.SelectedIndex).Text, _
            txtSearchText.Text, Not chkNonCaseSensetive.Checked, chkOnlyWholeWord.Checked, _
            chkSearchTextIsRegEx.Checked) = True Then
 
            bTextFound = True
            iLastFoundRowIndex = i
            objListview.Select()
            objListview.Items(i).Selected = True
            If objListview.MultiSelect = False Then Exit For
          End If
        Catch ex As Exception
          If chkSearchTextIsRegEx.Checked = True Then 
            MessageBox.Show("Fehler beim Auswerten des Regulären Ausdrucks!" & _
            Environment.NewLine & Environment.NewLine & _
            "Fehler:" & Environment.NewLine & ex.Message, _
            "Fehler bei Regulären-Ausdruck", MessageBoxButtons.OK, MessageBoxIcon.Error)
          End If
          bShowMsg = False
          ExitFor
        End Try
 
      Next i
    Else
 
      ' Suche über alle Spalten
      For i = iSearchStart To objListview.Items.Count - 1
        iLastCheckedRowIndex = i
 
        For y = 0 To objListview.Columns.Count - 1
          'Versteckte Spalten überspringen Option entsprechend eingestellt
          If bolIngnoreColumnsWithZeroWidth = True AndAlso _
            objListview.Columns(y).Width = 0 Then Continue For
 
          Try
            If ContainsPlus(objListview.Items(i).SubItems(y).Text, txtSearchText.Text, _
              Not chkNonCaseSensetive.Checked, chkOnlyWholeWord.Checked, _
              chkSearchTextIsRegEx.Checked) = True Then
 
              bTextFound = True
              iLastFoundRowIndex = i
              objListview.Select()
              objListview.Items(i).Selected = True
              If objListview.MultiSelect = False Then bExit = True
              ' Wenn eine Zeile Markiert wurde, müssen folgende Spalten 
              ' nicht mehr geprüft werden
              Exit For 
            End If
          Catch ex As Exception
            If chkSearchTextIsRegEx.Checked = True Then 
              MessageBox.Show("Fehler beim Auswerten des Regulären Ausdrucks!" & _
              Environment.NewLine & Environment.NewLine & _
              "Fehler:" & Environment.NewLine & ex.Message, _
              "Fehler bei Regulären-Ausdruck", MessageBoxButtons.OK, MessageBoxIcon.Error)
            End If
            bExit = True: Exit For
          End Try
        Next y
        If bExit Then Exit For 
      Next i
    End If
 
    ' Wenn Text nicht gefunde wurde, dann Meldung
    If bShowMsg Then
      If bTextFound = False Then
        MessageBox.Show("Der Text konnte mit den angegebenen Suchkriterien nicht " & _
          "in der Liste gefunden werden!", "Suche", MessageBoxButtons.OK, MessageBoxIcon.Information)
        Exit Sub
      End If
    End If
 
    ' Bei Multi-Select ListView Dialog schließen, bei Single-Select Liste scrollen
    If objListview.MultiSelect = True Then
      Me.DialogResult = System.Windows.Forms.DialogResult.OK
      Me.Close()
    Else
      If objListview.SelectedItems.Count <> 0 Then 
        objListview.EnsureVisible(objListview.SelectedItems(0).Index)
      End If
    End If
 
    ' Hauptformular in den Vordergrund bringen, damit Selektion sichtbar wird
    objMainForm.BringToFront()
  End Sub
  Private Sub Cancel_Button_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Cancel_Button.Click
 
    Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
    Me.Close()
  End Sub
  Private Sub frmFindInListView_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
 
    txtSearchText.Select()
    If objListview.MultiSelect = True Then 
      OK_Button.Text = "OK"
    Else
      OK_Button.Text = "Weitersuchen"
    End If
  End Sub
  Public Overloads Sub Show(ByVal oListview As ListView, _
    ByVal oMainForm As Form, _
    Optional ByVal bIngnoreColumnsWithZeroWidth As Boolean = False)
 
    ' Alle Spalten des Listviews in Auswahl aufnehmen
    Dim i As Integer
 
    objListview = oListview
    objMainForm = oMainForm
    bolIngnoreColumnsWithZeroWidth = bIngnoreColumnsWithZeroWidth
 
    For i = 0 To objListview.Columns.Count - 1
      If bIngnoreColumnsWithZeroWidth = True AndAlso _
        objListview.Columns(i).Width = 0 Then Continue For
      combSearchColumn.Items.Add(objListview.Columns(i).Text)
    Next i
    combSearchColumn.Items.Add("-- ALLE SPALTEN --")
    combSearchColumn.SelectedIndex = combSearchColumn.Items.Count - 1
 
    MyBase.Show(objListview)
  End Sub
  Public Overloads Sub ShowDialog(ByVal oListview As ListView, _
    ByVal oMainForm As Form, _
    Optional ByVal bIngnoreColumnsWithZeroWidth As Boolean = False)
 
    ' ShowDialog muss verwendet werden, damit Fokus wieder richtig 
    ' gesetzt wird, wenn Dialog beendet wird
    Show(oListview, oMainForm)
  End Sub
  ''' <summary>
  ''' Diese Funktion prüft, ob eine Bestimmte Zeichenfolge oder ein Wort in einem Text vorkommt
  ''' </summary>
  ''' <param name="WholeText">Kompletter Text in dem geprüft werden soll</param>
  ''' <param name="ContainText">Zu suchender Text</param>
  ''' <param name="bIgnoreCase">Wenn True, wird Gross-/Kleinschreibung ignoriert</param>
  ''' <param name="bOnlyWholeWords">Wenn True, muss 'ContainText' als komplettes Wort 
  ''' vorhanden sein und nicht nur in einer Zeichenkette vorkommen.</param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Private Function ContainsPlus(ByVal WholeText As String, _
    ByVal ContainText As String, _
    Optional ByVal bIgnoreCase As Boolean = True, _
    Optional ByVal bOnlyWholeWords As Boolean = False, _
    Optional ByVal bContainTextIsRegularExpression As Boolean = False) As String
 
    Dim tmpContainText As String = ""
 
    ' Wenn ContainText keine Regulären Ausdrücke enthält, dann ContainText in 
    ' ASCI-Codes für REGEX umsetzen, damit z.B. ] nicht als Regulärer-Ausdruck 
    ' interpretiert wird, sonstern als Text 
    If bContainTextIsRegularExpression = False Then
      Dim byteArray() As Byte
      Dim hexNumbers As System.Text.StringBuilder = New System.Text.StringBuilder
 
      byteArray = System.Text.ASCIIEncoding.ASCII.GetBytes(ContainText)
 
      For i As Integer = 0 To byteArray.Length - 1
        hexNumbers.Append("\x" & byteArray(i).ToString("x"))
      Next
      tmpContainText = hexNumbers.ToString
 
    Else
      tmpContainText = ContainText
    End If
 
    ' Patterns für ganzens Worte setzen (Wenn nur auf komplette Wörter geprüft werden soll)
    If bOnlyWholeWords = True Then tmpContainText = tmpContainText & "\b"
 
    If bIgnoreCase = True Then
      Return Regex.IsMatch(WholeText, tmpContainText, RegexOptions.IgnoreCase)
    Else
      Return Regex.IsMatch(WholeText, tmpContainText)
    End If
 
  End Functiony
  Private Sub txtSearchText_TextChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles txtSearchText.TextChanged
 
    bTextFound = False ' Zurück setzen das Text gefunden wurde
  End Sub
  Private Sub combSearchColumn_SelectedIndexChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles combSearchColumn.SelectedIndexChanged
 
    bTextFound = False ' Zurück setzen das Text gefunden wurde
  End Sub
  Private Sub chkNonCaseSensetive_CheckedChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles chkNonCaseSensetive.CheckedChanged
 
    bTextFound = False ' Zurück setzen das Text gefunden wurde
  End Sub
  Private Sub chkOnlyWholeWord_CheckedChanged(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles chkOnlyWholeWord.CheckedChanged
 
    bTextFound = False ' Zurück setzen das Text gefunden wurde
  End Sub
End Class

Erläuterungen zum Code:

  • Damit man eine beliebige ListView an den Suchdialog binden kann, habe ich die Show-Methode „überladen“ und erwartet als Parameter die Übergabe des ListViews sowie das Objekt des Hauptformulars (wird benötigt wenn die Liste kein MultiRow-Select erlaubt, um den Fokus auf das Hauptformular zu setzen.)
     
  • Da die String-Methode „Contains“ des Frameworks nicht alle meine Anforderungen abdeckte, hab ich die Funktion „ContainsPlus“ erstellt.
Diese prüft per Regulären-Ausdruck, ob ein Text in einem anderen vorkommt und erlaubt u.a. auch die Übergabe eines Regulären-Ausdrucks in unserem Suchdialog.

Weitere Funktion des „ListViewEx“:
Im Beispielprojekt zu diesem Workshop ist ein Projekt enthalten, das das ListView Control abgesehen vom Suchdialog noch um folgende Funktionen erweitert:

  • SaveSelectedKeys
    (Speichert die Keys der selektierten Zeilen in einer Collection - Können Sie dazu verwenden, um die Selektion vor neu Laden der Liste zu speichern und nach neu Laden der Liste per „RestoreSelectedKey“ wieder die „alten Einträge“ zu selektieren)
     
  • RestoreSelectedKeys
    (Stellt die Selektion von Listview.Items wieder her)
     
  • ContainsKey
    (Liefert True zurück, wenn der übergeben Key in der Listview gefunden wird)
     
  • SelectByKey
    (Sucht nach dem übergebenen Key und selektiert den Eintrag wenn dieser gefunden wird)
     
  • SetRowForeColor
    (Setzt die Schriftfarbe für eine ganze Zeile)
     
  • SetRowBackColor
    (Setzt die Hintergrundfarbe für eine ganze Zeile)
     
  • SaveToFile
    (Speichert den Inhalt der ListView in eine Textdatei)
     
  • StopRefresh
    (Aufruf von "BeginUpdate" und "SuspendLayout")
     
  • ResumeRefresh
    (Aufruf von "ResumeLayout" und "EndUpdate"
     
  • DeleteRowsThatDontContainsString
    (Löschte alle Zeilen, die einen bestimmten Texte in Spalte X nicht enthalten)

1. ListViewEx verwenden

1.1 Control „ListViewEx“ in Ihr Projekt einbinden
Kopieren Sie die Datei „ListviewEx.dll“ an eine beliebige Stelle z.B. unter: „C:\Programme\Microsoft Visual Studio 9.0\Common7\IDE“.

Darauf fügen Sie die DLL der Toolbox in Visual-Studio hinzu.
Ab sofort können Sie das das Control wie die normale ListView Ihrer Form per Drag & Drop hinzufügen.

1.2 Aufruf des Suchdialogs
Wenn Sie bereits wie in Punkt 1.1 beschrieben das ListViewEx als Control verwenden, können Sie den Suchdialog wie folgt aufrufen:

  • Fügen Sie Ihrem Formular ein

    Imports ListViewExtended

    hinzu und verwenden folgende Zeilen (z.B. bei Key-Down Event von Strg+F), um den Suchdialog einzublenden:

    Dim ofrmFindInListView As frmFindInListView = New frmFindInListView
    ofrmFindInListView.Show(ListViewEx1, Me)

    Daraufhin erscheint der Suchdialog, der mit allen verfügbaren Spalten Ihres Listviews gefüllt ist.

     Download ListViewEx (115 KB)



  • Anzeige

    Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
    (einschl. Beispielprojekt!)

    Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
    - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
    - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
    Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
     
     
    Copyright ©2000-2019 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


    Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.