I\'m trying to create a TreeView
that will bold portions of node text that match a search term. My code was adopted from this question. I have
Part of the information found in the previous question didn't make the cut.
TextFormatFlags.NoClipping
from the original code, not good, this is a keeper. Unless you actually want to clip the Text, but then you need to specify how to clip it. Other flags can be combined for that.Specific to this question:
MeasureText
).e.Node.Bounds.X
. In code, it's stored in int drawingPosition = e.Node.Bounds.X;
.Visual result:
TextFormatFlags twFormat = TextFormatFlags.Left | TextFormatFlags.VerticalCenter |
TextFormatFlags.NoClipping | TextFormatFlags.NoPadding;
private void tree_DrawNode(object sender, DrawTreeNodeEventArgs e)
{
Color textColor = e.Node.ForeColor;
Color backColor = e.Node.BackColor == Color.Empty ? tree.BackColor : e.Node.BackColor;
if (e.State.HasFlag(TreeNodeStates.Selected) || e.State.HasFlag(TreeNodeStates.Focused)) {
textColor = SystemColors.HighlightText;
backColor = SystemColors.Highlight;
}
using (var brush = new SolidBrush(backColor)) {
e.Graphics.FillRectangle(brush, e.Bounds);
}
string searchText = fieldSearch.Text; // Search string from TextBox
int drawingPosition = e.Node.Bounds.X;
foreach (var part in BuildDrawingString(e.Node.Text, searchText)) {
var style = part.Selected ? FontStyle.Bold : FontStyle.Regular;
drawingPosition += RenderNodeText(part.Text, e, style, new Point(drawingPosition, e.Node.Bounds.Y), textColor).Width;
}
}
private Size RenderNodeText(string text, DrawTreeNodeEventArgs e, FontStyle altStyle, Point offset, Color foreColor)
{
using (var font = new Font(tree.Font, altStyle)) {
var size = e.Node.Bounds.Size;
var textWidth = TextRenderer.MeasureText(e.Graphics, text, font, size, twFormat);
var rect = new Rectangle(offset, size);
TextRenderer.DrawText(e.Graphics, text, font, rect, foreColor, e.Node.BackColor, twFormat);
return textWidth;
}
}
private IEnumerable<(string Text, bool Selected)> BuildDrawingString(string itemContent, string pattern)
{
if (pattern.Length == 0) {
yield return (itemContent, false);
}
else {
var matches = Regex.Split(itemContent, $"(?i){pattern}");
int pos = itemContent.IndexOf(pattern, StringComparison.CurrentCultureIgnoreCase);
for (int i = 0; i < matches.Length; i++) {
if (matches[i].Length == 0 && i < matches.Length - 1) {
yield return (itemContent.Substring(pos, pattern.Length), matches[i].Length > 0 ? false : true);
}
else {
yield return (matches[i], false);
if (i < matches.Length - 1) {
yield return (itemContent.Substring(pos, pattern.Length), true);
}
}
}
}
}