-
Jul 20th, 2018, 06:07 PM
#1
Thread Starter
Hyperactive Member
Highlighting just certain text in treeView node text
I have a treeview that lists matches that are varying degrees of closeness to the original text. The nade is the original search text and the child nodes are the suggested matches. What I want to do is highlight the words in the suggested matches (child nodes) that appear in the parent node.
Example:
Parent Node: Testing the ability to match text.
Child node: Testing my car's ability to accelerate.
In this example the words "Testing", "ability", "to" would be highlighted in the node text.
Does someone know a way to highlight just certain words in a treeview node text?
Thanks,
Eric
--------------------------------------------------------------------------------------------------------------------
VB.net/C# ... Visual Studio 2019
"None of us are as smart as all of us."
-
Jul 20th, 2018, 09:25 PM
#2
Re: Highlighting just certain text in treeView node text
You would set the DrawMode property of the TreeView to either OwnerDrawText or OwnerDrawAll, handle the DrawNode event and then use GDI+ to draw the text of the node yourself with the desired highlighting.
If you use OwnerDrawText then you would be able to use a different text colour for the matching words. I'm not sure whether OwnerDrawText supports using a different background colour or not. If it doesn't and that's what you want then you would have to use OwnerDrawAll, which also means that you need more drawing code.
I would suggest that you start out by getting the GDI+ stuff working on a simple form. Start with a method like this:
Code:
Private Sub DrawTextWithHighlightedWords(text As String, wordsToHighlight As String(), g As Graphics, location As Point)
'Split text into substrings to highlight and not highlight.
'Draw substrings using g starting at location.
End Sub
You can call that method from the Paint event handler of the form and experiment with your code until you get the effect you want. You can then start calling that method from the DrawNode event of your TreeView.
-
Jul 20th, 2018, 10:12 PM
#3
Re: Highlighting just certain text in treeView node text
Here's something to start with:
Code:
Public Class Form1
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
DrawTextWithHighlightedWords("girls just want to have fun",
{"just", "to", "have"},
e.Graphics,
New PointF(10.0F, 10.0F))
End Sub
Private Sub DrawTextWithHighlightedWords(text As String, wordsToHighlight As String(), g As Graphics, location As PointF)
'Split text into substrings to highlight and not highlight.
Dim wordsWithHighlightStatus = GetWordsWithHighlightStatus(text, wordsToHighlight)
'Draw substrings using g starting at location.
For Each word In wordsWithHighlightStatus
g.DrawString(word.Item1,
Font,
If(word.Item2, Brushes.Red, Brushes.Black),
location.X,
location.Y)
Dim textSize = g.MeasureString(word.Item1, Font)
location = New PointF(location.X + textSize.Width,
location.Y)
Next
End Sub
Private Function GetWordsWithHighlightStatus(text As String, wordsToHighlight As String()) As Tuple(Of String, Boolean)()
Dim words = text.Split(" "c)
Dim wordsWithHighlightStatus = Array.ConvertAll(words,
Function(s) Tuple.Create(s, wordsToHighlight.Contains(s)))
Return wordsWithHighlightStatus
End Function
End Class
Last edited by jmcilhinney; Jul 20th, 2018 at 10:16 PM.
-
Jul 20th, 2018, 10:43 PM
#4
Re: Highlighting just certain text in treeView node text
Here's something a bit better. The spaces between the words got bigger with each word using MeasureString but MeasureCharacterRanges keeps those spaces even.
Code:
Public Class Form1
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
DrawTextWithHighlightedWords("girls just want to have fun",
{"just", "to", "have"},
e.Graphics,
New PointF(10.0F, 100.0F))
End Sub
Private Sub DrawTextWithHighlightedWords(text As String, wordsToHighlight As String(), g As Graphics, location As PointF)
'Split text into substrings to highlight and not highlight.
Dim words = text.Split(" "c)
Dim wordsWithHighlightStatus = Array.ConvertAll(words,
Function(s) Tuple.Create(s, wordsToHighlight.Contains(s)))
Dim ranges = GetCharacterRanges(words)
Dim format As New StringFormat
format.SetMeasurableCharacterRanges(ranges)
Dim regions = g.MeasureCharacterRanges(text,
Font,
New RectangleF(location.X, location.Y,
ClientSize.Width - location.X,
ClientSize.Height - location.Y),
format)
For i = 0 To wordsWithHighlightStatus.GetUpperBound(0)
Dim wordWithHighlightStatus = wordsWithHighlightStatus(i)
Dim region = regions(i)
g.DrawString(wordWithHighlightStatus.Item1,
Font,
If(wordWithHighlightStatus.Item2, Brushes.Red, Brushes.Black),
region.GetBounds(g).Location)
Next
End Sub
Private Function GetCharacterRanges(words As String()) As CharacterRange()
Dim startIndex = 0
Dim ranges As New List(Of CharacterRange)
For i = 0 To words.GetUpperBound(0)
If i > 0 Then
startIndex += 1
End If
Dim wordLength = words(i).Length
ranges.Add(New CharacterRange(startIndex, wordLength))
startIndex += wordLength
Next
Return ranges.ToArray()
End Function
End Class
-
Jul 20th, 2018, 10:54 PM
#5
Re: Highlighting just certain text in treeView node text
This will get you closer but there is still some work to do.
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim node1 As New TreeNode("I would just like to go and have a meal")
node1.Nodes.Add(New TreeNode("girls just want to have fun"))
Dim node2 As New TreeNode("Testing the ability to match text")
node2.Nodes.Add(New TreeNode("Testing my car's ability to accelerate"))
TreeView1.Nodes.AddRange({node1, node2})
End Sub
Private Sub TreeView1_DrawNode(sender As Object, e As DrawTreeNodeEventArgs) Handles TreeView1.DrawNode
Dim parentNode = e.Node.Parent
If parentNode Is Nothing Then
e.DrawDefault = True
Else
Dim wordsToHighlight = parentNode.Text.Split(" "c)
DrawTextWithHighlightedWords(e.Node.Text, wordsToHighlight, e.Graphics, e.Bounds.Location)
End If
End Sub
Private Sub DrawTextWithHighlightedWords(text As String, wordsToHighlight As String(), g As Graphics, location As PointF)
'Split text into substrings to highlight and not highlight.
Dim words = text.Split(" "c)
Dim wordsWithHighlightStatus = Array.ConvertAll(words,
Function(s) Tuple.Create(s, wordsToHighlight.Contains(s)))
Dim ranges = GetCharacterRanges(words)
Dim format As New StringFormat
format.SetMeasurableCharacterRanges(ranges)
Dim regions = g.MeasureCharacterRanges(text,
Font,
New RectangleF(location.X, location.Y,
ClientSize.Width - location.X,
ClientSize.Height - location.Y),
format)
For i = 0 To wordsWithHighlightStatus.GetUpperBound(0)
Dim wordWithHighlightStatus = wordsWithHighlightStatus(i)
Dim region = regions(i)
g.DrawString(wordWithHighlightStatus.Item1,
Font,
If(wordWithHighlightStatus.Item2, Brushes.Red, Brushes.Black),
region.GetBounds(g).Location)
Next
End Sub
Private Function GetCharacterRanges(words As String()) As CharacterRange()
Dim startIndex = 0
Dim ranges As New List(Of CharacterRange)
For i = 0 To words.GetUpperBound(0)
If i > 0 Then
startIndex += 1
End If
Dim wordLength = words(i).Length
ranges.Add(New CharacterRange(startIndex, wordLength))
startIndex += wordLength
Next
Return ranges.ToArray()
End Function
End Class
-
Jul 23rd, 2018, 08:08 AM
#6
Thread Starter
Hyperactive Member
Re: Highlighting just certain text in treeView node text
jmcilhinney. As always. Exceptional. Thanks for all your help over the years. I just learned a whole lot in this post alone.
Thanks,
Eric
--------------------------------------------------------------------------------------------------------------------
VB.net/C# ... Visual Studio 2019
"None of us are as smart as all of us."
-
Jul 23rd, 2018, 09:03 AM
#7
Re: Highlighting just certain text in treeView node text
I did notice an odd bug with that code where the text of the second child node was drawn over the first node. I looked a bit closer and that can be alleviated by changing this line:
Code:
If parentNode Is Nothing Then
to this:
Code:
If parentNode Is Nothing OrElse e.Bounds.X = -1 Then
You probably also noticed that the text of the owner-drawn nodes overhangs the selection box a bit. I'm afraid that I don't know how to fix that. It just seems like GDI+ doesn't draw the text the same way as the default control as all the words seem to be just a bit wider. It can be improved a bit by changing this:
Code:
Dim format As New StringFormat
to this:
Code:
Dim format = StringFormat.GenericTypographic
but, to do any better, you may have to do a bit of research on GDI+ and, if possible, how the TreeView is rendered by default.
-
Jul 23rd, 2018, 09:26 AM
#8
Re: Highlighting just certain text in treeView node text
Originally Posted by jmcilhinney
You probably also noticed that the text of the owner-drawn nodes overhangs the selection box a bit. I'm afraid that I don't know how to fix that. It just seems like GDI+ doesn't draw the text the same way as the default control as all the words seem to be just a bit wider. It can be improved a bit by changing this:
I thought that the text in WinForm Controls from .NET 2.0 onwards was rendered by GDI, not GDI+.
You'd probably get more consistent output using the TextRenderer.MeasureText and TextRenderer.DrawText methods, passing the relevant e.Graphics to the methods' IDeviceContext parameter.
-
Jul 23rd, 2018, 10:04 AM
#9
Re: Highlighting just certain text in treeView node text
Originally Posted by Inferrd
You'd probably get more consistent output using the TextRenderer.MeasureText and TextRenderer.DrawText methods, passing the relevant e.Graphics to the methods' IDeviceContext parameter.
I did look at that but there's no equivalent to MeasureCharacterRanges so it would be a bit more complex. If it fixes the issue though, it might be necessary. Decided to have a closer look and, while this is still not perfect, it's a significant improvement:
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim node1 As New TreeNode("I would just like to go and have a meal")
node1.Nodes.Add(New TreeNode("girls just want to have fun"))
Dim node2 As New TreeNode("Testing the ability to match text")
node2.Nodes.Add(New TreeNode("Testing my car's ability to accelerate"))
TreeView1.Nodes.AddRange({node1, node2})
End Sub
Private Sub TreeView1_DrawNode(sender As Object, e As DrawTreeNodeEventArgs) Handles TreeView1.DrawNode
Dim parentNode = e.Node.Parent
If parentNode Is Nothing OrElse e.Bounds.X = -1 Then
e.DrawDefault = True
Else
Dim wordsToHighlight = parentNode.Text.Split(" "c)
DrawTextWithHighlightedWords(e.Node.Text, wordsToHighlight, e.Graphics, e.Bounds.Location, (e.State And TreeNodeStates.Selected) = TreeNodeStates.Selected)
End If
End Sub
Private Sub DrawTextWithHighlightedWords(text As String, wordsToHighlight As String(), g As Graphics, location As Point, selected As Boolean)
'Split text into substrings to highlight and not highlight.
Dim words = text.Split(" "c)
Dim wordsWithHighlightStatus = Array.ConvertAll(words,
Function(s) Tuple.Create(s, wordsToHighlight.Contains(s)))
For i = 0 To wordsWithHighlightStatus.GetUpperBound(0)
Dim wordWithHighlightStatus = wordsWithHighlightStatus(i)
Dim word = wordWithHighlightStatus.Item1
Dim highlight = wordWithHighlightStatus.Item2
Dim font = TreeView1.Font
Dim size = TextRenderer.MeasureText(g,
word,
font,
Drawing.Size.Empty,
TextFormatFlags.NoPadding)
TextRenderer.DrawText(g,
word,
font,
location,
If(highlight,
Color.Red,
If(selected,
Color.White,
Color.Black)),
TextFormatFlags.NoPadding)
location.Offset(size.Width, 0)
size = TextRenderer.MeasureText(g,
" ",
font,
Drawing.Size.Empty,
TextFormatFlags.NoPadding)
TextRenderer.DrawText(g,
" ",
font,
location,
Color.Black,
TextFormatFlags.NoPadding)
location.Offset(size.Width, 0)
Next
End Sub
End Class
-
Jul 23rd, 2018, 11:13 AM
#10
Re: Highlighting just certain text in treeView node text
This iteration looks pretty darn good:
Code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim node1 As New TreeNode("I would just like to go and have a meal")
node1.Nodes.Add(New TreeNode("girls just want to have fun"))
Dim node2 As New TreeNode("Testing the ability to match text")
node2.Nodes.Add(New TreeNode("Testing my car's ability to accelerate"))
TreeView1.Nodes.AddRange({node1, node2})
End Sub
Private Sub TreeView1_DrawNode(sender As Object, e As DrawTreeNodeEventArgs) Handles TreeView1.DrawNode
Dim parentNode = e.Node.Parent
If parentNode Is Nothing OrElse e.Bounds.X = -1 Then
e.DrawDefault = True
Else
Dim wordsToHighlight = parentNode.Text.Split(" "c)
DrawTextWithHighlightedWords(e.Node.Text,
wordsToHighlight,
e.Graphics,
e.Bounds,
(e.State And TreeNodeStates.Selected) = TreeNodeStates.Selected)
End If
End Sub
Private Sub DrawTextWithHighlightedWords(text As String, wordsToHighlight As String(), g As Graphics, bounds As Rectangle, selected As Boolean)
Dim textSize = GetTextSize(g, text)
'Default location is the node bounds location.
Dim location = bounds.Location
'Add padding to the left of the text, which is half the difference between the node bounds width and the text width.
location.Offset((bounds.Width - textSize.Width) \ 2, 0)
'Split text into substrings to highlight and not highlight.
Dim words = text.Split(" "c)
Dim wordsWithHighlightStatus = Array.ConvertAll(words,
Function(s) Tuple.Create(s, wordsToHighlight.Contains(s)))
For i = 0 To wordsWithHighlightStatus.GetUpperBound(0)
Dim wordWithHighlightStatus = wordsWithHighlightStatus(i)
Dim word = wordWithHighlightStatus.Item1
Dim highlight = wordWithHighlightStatus.Item2
Dim size As Size
'Draw a space before all but the first word.
If i > 0 Then
size = GetTextSize(g, " ")
DrawText(g,
" ",
location,
Color.Black)
location.Offset(size.Width, 0)
End If
'Draw the current word.
size = GetTextSize(g, word)
DrawText(g,
word,
location,
If(highlight,
Color.Red,
If(selected,
Color.White,
Color.Black)))
location.Offset(size.Width, 0)
Next
End Sub
Private Function GetTextSize(g As Graphics, text As String) As Size
Return TextRenderer.MeasureText(g,
text,
TreeView1.Font,
Size.Empty,
TextFormatFlags.NoPadding)
End Function
Private Sub DrawText(g As Graphics, text As String, location As Point, color As Color)
TextRenderer.DrawText(g,
text,
TreeView1.Font,
location,
color,
TextFormatFlags.NoPadding)
End Sub
End Class
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|