Highlighting Multiple Words in a Windows.Forms.ListBox

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
Hi,
I had fun playing with ideas for the high light word in a listbox thread. Now Ive created a control (inheriting form Windows.Forms.ListBox) that encapsulates some of the features mentioned in the post. Feel free to use the code and comment on it.
The only thing I know I need some help with is double buffering the items to avoid flicker, although it may not be that bad.
Heres the code:
SearchableListBox.vb Imports System.Text.RegularExpressions
Public Class SearchableListBox
Inherits ListBox
Private _searchText As String
Public Property SearchText As String
Get
Return _searchText
End Get
Set(ByVal value As String)
_searchText = value
Refresh()
End Set
End Property
Protected Overrides Sub OnMeasureItem(e As System.Windows.Forms.MeasureItemEventArgs)
If Me.Items.Count = 0 Then
Exit Sub
End If
Dim text = Me.Items(e.Index).ToString()
Dim stringSize As SizeF = e.Graphics.MeasureString(text, Font)
e.ItemHeight = CInt(stringSize.Height)
e.ItemWidth = CInt(stringSize.Width)
End Sub
Protected Overrides Sub OnDrawItem(e As System.Windows.Forms.DrawItemEventArgs)
Debug.WriteLine("Draw Item: " & e.Index)
e.DrawBackground()
We dont have to do any hilighting if there are no items
If Me.Items.Count = 0 Then
Show the name of the SearchListBox as the first item in design mode
If Me.DesignMode Then
e.Graphics.DrawString(Me.Name, e.Font, Brushes.Black, e.Bounds, StringFormat.GenericDefault)
End If
Exit Sub
End If
Dim lForeBrush As Brush = New SolidBrush(e.ForeColor)
Dim lHilightBrush As Brush = New SolidBrush(Color.FromArgb(&H98, &HE9, &HE9, &HE))
Dim lItemText As String = Me.Items(e.Index).ToString()
Dim lMatches = GetRegExMatches(lItemText, Me.SearchText)
Dim lItemStringFormat As New StringFormat With {.FormatFlags = StringFormatFlags.NoClip}
If lMatches IsNot Nothing Then
Dim lCharRanges As New List(Of CharacterRange)
Dim lSafeSearchText As String = Nothing
Convert matchs to List(of CharacterRange)
For Each match As Match In lMatches
If match.Success AndAlso match.Length > 0 Then
lCharRanges.Add(New CharacterRange(match.Index, match.Length))
End If
Next match
lItemStringFormat.SetMeasurableCharacterRanges(lCharRanges.ToArray)
Dim matchedregion() As Region = e.Graphics.MeasureCharacterRanges(lItemText, e.Font, e.Bounds, lItemStringFormat)
For Each rgn As Region In matchedregion
Dim lMatchedWordRegion = rgn.GetBounds(e.Graphics)
lMatchedWordRegion.Inflate(0, -1)
e.Graphics.FillRectangle(lHilightBrush, Rectangle.Round(lMatchedWordRegion))
rgn.Dispose()
Next rgn
End If
e.Graphics.DrawString(lItemText, e.Font, lForeBrush, e.Bounds, lItemStringFormat)
e.DrawFocusRectangle()
lForeBrush.Dispose()
lHilightBrush.Dispose()
End Sub
Friend Shared Function GetRegExMatches(ByVal inputText As String, ByVal searchText As String) As MatchCollection
If String.IsNullOrWhiteSpace(searchText) Then
Return Nothing
End If
Dim lSafeSearchText As String
Break apart the search into string array split by one or more space
Dim lWords() As String = Regex.Split(searchText, "s+")
Join the words back togeter to form a new RegEx with the escaped words separate by the OR operator (|)
Dim sb As New System.Text.StringBuilder
For Each lWord As String In lWords
Escape the word in case there are any RegEx metacharacters in the string
sb.Append(Regex.Escape(lWord))
Build OR
sb.Append("|")
Next lWord
If sb.Length > 0 Then
Convert to string stripping the extra |
lSafeSearchText = sb.ToString(0, sb.Length - 1)
Return Regex.Matches(inputText, lSafeSearchText, RegexOptions.IgnoreCase)
Else
Return Nothing
End If
End Function
Force OwnerDrawVariable
Public Overrides Property DrawMode As System.Windows.Forms.DrawMode
Get
Return Windows.Forms.DrawMode.OwnerDrawVariable
End Get
Set(value As System.Windows.Forms.DrawMode)
MyBase.DrawMode = Windows.Forms.DrawMode.OwnerDrawVariable
End Set
End Property
End Class

Form1.vb
Option Strict On
Option Explicit On
Option Infer On
Imports System.Text.RegularExpressions
Public Class SearchableListBoxTestForm
Inherits System.Windows.Forms.Form
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.Panel1 = New System.Windows.Forms.Panel()
Me.SearchTextBox = New System.Windows.Forms.TextBox()
Me.SearhButton = New System.Windows.Forms.Button()
Me.SearchableListBox1 = New WindowsApplication1.SearchableListBox()
Me.Panel1.SuspendLayout()
Me.SuspendLayout()

Panel1

Me.Panel1.Controls.Add(Me.SearchTextBox)
Me.Panel1.Controls.Add(Me.SearhButton)
Me.Panel1.Dock = System.Windows.Forms.DockStyle.Top
Me.Panel1.Location = New System.Drawing.Point(0, 0)
Me.Panel1.Name = "Panel1"
Me.Panel1.Size = New System.Drawing.Size(395, 36)
Me.Panel1.TabIndex = 1

SearchTextBox

Me.SearchTextBox.Location = New System.Drawing.Point(13, 7)
Me.SearchTextBox.Name = "SearchTextBox"
Me.SearchTextBox.Size = New System.Drawing.Size(289, 20)
Me.SearchTextBox.TabIndex = 1

SearhButton

Me.SearhButton.Location = New System.Drawing.Point(308, 7)
Me.SearhButton.Name = "SearhButton"
Me.SearhButton.Size = New System.Drawing.Size(75, 23)
Me.SearhButton.TabIndex = 2
Me.SearhButton.Text = "&Search"
Me.SearhButton.UseVisualStyleBackColor = True

SearchableListBox1

Me.SearchableListBox1.Dock = System.Windows.Forms.DockStyle.Fill
Me.SearchableListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable
Me.SearchableListBox1.FormattingEnabled = True
Me.SearchableListBox1.Location = New System.Drawing.Point(0, 36)
Me.SearchableListBox1.Name = "SearchableListBox1"
Me.SearchableListBox1.SearchText = Nothing
Me.SearchableListBox1.Size = New System.Drawing.Size(395, 226)
Me.SearchableListBox1.TabIndex = 3

Form3

Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(395, 262)
Me.Controls.Add(Me.SearchableListBox1)
Me.Controls.Add(Me.Panel1)
Me.Name = "Form3"
Me.Text = "SearchableListBox Test"
Me.Panel1.ResumeLayout(False)
Me.Panel1.PerformLayout()
Me.ResumeLayout(False)
End Sub
Friend WithEvents Panel1 As System.Windows.Forms.Panel
Friend WithEvents SearchTextBox As System.Windows.Forms.TextBox
Friend WithEvents SearhButton As System.Windows.Forms.Button
Friend WithEvents SearchableListBox1 As WindowsApplication1.SearchableListBox
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
SearchableListBox1.DrawMode = DrawMode.OwnerDrawVariable
Borrowed from https://en.wikipedia.org/wiki/Hitchhike
SearchableListBox1.Items.Add("1934 - It Happened One Night")
SearchableListBox1.Items.Add("1937 - Way Out West")
SearchableListBox1.Items.Add("1945 - Detour")
SearchableListBox1.Items.Add("1953 - The Hitch-Hiker")
SearchableListBox1.Items.Add("1963 - Its a Mad, Mad, Mad, Mad World")
SearchableListBox1.Items.Add("1974 - The Texas Chain Saw Massacre")
SearchableListBox1.Items.Add("1977 - Hitch-Hike")
SearchableListBox1.Items.Add("1981 - Roadgames")
SearchableListBox1.Items.Add("1983 - Going Back")
SearchableListBox1.Items.Add("1985 - Pee-wees Big Adventure")
SearchableListBox1.Items.Add("1985 - The Sure Thing")
SearchableListBox1.Items.Add("1986 - The Hitcher")
SearchableListBox1.Items.Add("1989 - Kikis Delivery Service")
SearchableListBox1.Items.Add("1990 - Nouvelle Vague")
SearchableListBox1.Items.Add("1994 - Even Cowgirls Get the Blues")
SearchableListBox1.Items.Add("1998 - Something About Mary")
SearchableListBox1.Items.Add("2000 - O Brother, Where Art Thou?")
SearchableListBox1.Items.Add("2001 - 3000 Miles to Graceland")
SearchableListBox1.Items.Add("2001 - Jay and Silent Bob Strike Back")
SearchableListBox1.Items.Add("2003 - The Hitcher II: Ive Been Waiting")
SearchableListBox1.Items.Add("2003 - The Haunted Mansion")
SearchableListBox1.Items.Add("2004 - EuroTrip")
SearchableListBox1.Items.Add("2004 - Crash")
SearchableListBox1.Items.Add("2004 - Riding the Bullet")
SearchableListBox1.Items.Add("2005 - The Hitchhikers Guide to the Galaxy")
SearchableListBox1.Items.Add("2007 - The Hitcher")
SearchableListBox1.Items.Add("2007 - Mr. Beans Holiday")
SearchableListBox1.Items.Add("2007 - Into the Wild")
SearchableListBox1.Items.Add("2007 - Have Dreams, Will Travel")
SearchableListBox1.Items.Add("2008 - The Yellow Handkerchief")
SearchableListBox1.Items.Add("2013 - Man of Steel (film)")
End Sub
Private Sub SearhButton_Click(sender As System.Object, e As System.EventArgs) Handles SearhButton.Click
SearchableListBox1.SearchText = SearchTextBox.Text
End Sub
Private Sub SearchTextBox_TextChanged(sender As System.Object, e As System.EventArgs) Handles SearchTextBox.TextChanged
SearchableListBox1.SearchText = SearchTextBox.Text
End Sub
End Class

Sorry about posing so much code, at least its all here.

View the full article
 
Back
Top