Rubrik: .NET | VB-Versionen: VB2005, VB2008 | 04.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 Stuplich | Bewertung: | Views: 15.359 |
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:
Control | Name |
Label | Label1 |
Label | Label2 |
TextBox | txtSearchText |
ComboBox | combSearchColumn |
CheckBox | chkNonCaseSensetive |
CheckBox | chkOnlyWholeWord |
CheckBox | chkSearchTextIsRegEx |
Button | OK_Button |
Button | Cancel_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.
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:
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)