如何使用图形突出显示控件中的包装文本?

我需要使用fill rect突出显示控件中的特定字符。 我可以通过使用如下的Graphics.MeasureString()方法获取文本的位置,如下所示,

 var size = g.MeasureString(tempSearchText, style.Font, 0, StringFormat.GenericTypographic); 

在此处输入图像描述

如果文本被包装,那么我无法找到该字符的确切边界以突出显示该文本。

在此处输入图像描述

我需要在包装的文本中获得给定字符的确切界限。 提供您的建议以实现此方案。

目前没有明确规定哪些控件,所以我测试了3种不同的控件:
TextBoxRichTextboxListBox

TextBox和RichTextbox具有相同的行为并共享相同的工具,因此无需定义两种不同的方法来实现相同的结果。
当然,RichTextbox提供了更多选项,包括RTF 。

另外,我正在测试Graphics.DrawString()TextRenderer.DrawText()

这是此测试的结果,因此更清楚代码的作用。

在此处输入图像描述

警告
对于此示例,我使用的是Control.CreateGraphics() ,因为TextBoxRichTextBox控件不提供Paint()事件。 对于真实世界的应用程序,您应该创建一个派生自TextBoxRichTextBox的自定义控件并覆盖OnPaint()方法

1)突出显示多行TextBox控件中的所有t

TextRenderer-> DrawText的():

 //Define some useful flags for TextRenderer TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.Top | TextFormatFlags.NoPadding | TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl; //The char to look for char TheChar = 't'; //Find all 't' chars indexes in the text List TheIndexList = textBox1.Text.Select((chr, idx) => chr == TheChar ? idx : -1) .Where(idx => idx != -1).ToList(); //Or with Regex - same thing, pick the one you like best List TheIndexList = Regex.Matches(textBox1.Text, TheChar.ToString()) .Cast() .Select(chr => chr.Index).ToList(); //Using .GetPositionFromCharIndex(), define the Point [p] where the highlighted text is drawn if (TheIndexList.Count > 0) { foreach (int Position in TheIndexList) { Point p = textBox1.GetPositionFromCharIndex(Position); using (Graphics g = textBox1.CreateGraphics()) TextRenderer.DrawText(g, TheChar.ToString(), textBox1.Font, p, textBox1.ForeColor, Color.LightGreen, flags); } } 

使用Graphics.FillRectangle()Graphics.DrawString()的相同操作:

 if (TheIndexList.Count > 0) { using (Graphics g = textBox1.CreateGraphics()) { foreach (int Position in TheIndexList) { PointF pF = textBox1.GetPositionFromCharIndex(Position); SizeF sF = g.MeasureString(TheChar.ToString(), textBox1.Font, 0, StringFormat.GenericTypographic); g.FillRectangle(Brushes.LightGreen, new RectangleF(pF, sF)); using (SolidBrush brush = new SolidBrush(textBox1.ForeColor)) { g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; g.DrawString(TheChar.ToString(), textBox1.Font, brush, pF, StringFormat.GenericTypographic); } } } } 

行为没有显着差异: TextRenderer.DrawText()Graphics.DrawString()在这里执行完全相同的操作。
Application.SetCompatibleTextRenderingDefault()设置为truefalse似乎没有任何影响(至少在当前上下文中)。

2)突出显示TextBox控件和多行RichTextbox控件中的一些字符串模式(“Words”)。

仅使用TextRenderer ,因为行为没有区别。

我只是让IndexOf()找到第一次出现的字符串,但之前使用的相同搜索模式可以取而代之。 正则表达式更好。

 string[] TheStrings = {"for", "s"}; foreach (string pattern in TheStrings) { Point p = TextBox2.GetPositionFromCharIndex(TextBox2.Text.IndexOf(pattern)); TextRenderer.DrawText(TextBox2.CreateGraphics(), pattern, TextBox2.Font, p, TextBox2.ForeColor, Color.LightSkyBlue, flags); } TheStrings = new string []{"m", "more"}; foreach (string pattern in TheStrings) { Point p = richTextBox1.GetPositionFromCharIndex(richTextBox1.Text.IndexOf(pattern)); using (Graphics g = richTextBox1.CreateGraphics()) TextRenderer.DrawText(g, pattern, richTextBox1.Font, p, richTextBox1.ForeColor, Color.LightSteelBlue, flags); } 

3)突出显示ListBox控件的所有ListItems中的所有s (当然它可以是任何其他字符串:)

ListBox.DrawMode设置为Normal并“动态”更改为OwnerDrawVariable以评估TextRendererGraphics在此处的行为是否不同。

存在一个小的差异:相对于ListBox的左边距,与标准实现相比,具有不同的偏移。 TextRenderer, TextFormatFlags.NoPadding向左渲染2个像素(相反没有标记)。 图形向右渲染1个像素。
当然,如果在设计模式下设置了OwnerDrawVariable ,则不会注意到这一点。

 string HighLightString = "s"; int GraphicsPaddingOffset = 1; int TextRendererPaddingOffset = 2; private void button1_Click(object sender, EventArgs e) { listBox1.DrawMode = DrawMode.OwnerDrawVariable; } 

以下代码的工作原理如下:
1)获取ListItem文本中出现模式( string HighLightString )的所有位置。
2)使用模式的位置和长度定义CharacterRange结构数组。
3)使用.SetMeasurableCharacterRanges()使用所有CharacterRange结构填充StringFormat
4)使用Graphics.MeasureCharacterRanges()传递初始化的StringFormat来定义区域数组。
5)使用Region.GetBounds()定义一个大小的矩形数组
6)使用Graphics.FillRectangles()用高亮颜色填充所有矩形
7)绘制ListItem文本。

TextRenderer.DrawText()实现:

 private void listBox1_DrawItem(object sender, DrawItemEventArgs e) { e.DrawBackground(); TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.Top | TextFormatFlags.NoPadding | TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl; Rectangle bounds = new Rectangle(e.Bounds.X + TextRendererPaddingOffset, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height); string ItemString = listBox1.GetItemText(listBox1.Items[e.Index]); List TheIndexList = Regex.Matches(ItemString, HighLightString) .Cast() .Select(s => s.Index).ToList(); if (TheIndexList.Count > 0) { CharacterRange[] CharRanges = new CharacterRange[TheIndexList.Count]; for (int CharX = 0; CharX < TheIndexList.Count; CharX++) CharRanges[CharX] = new CharacterRange(TheIndexList[CharX], HighLightString.Length); StringFormat format = new StringFormat(StringFormat.GenericDefault); format.SetMeasurableCharacterRanges(CharRanges); Region[] regions = e.Graphics.MeasureCharacterRanges(ItemString, e.Font, e.Bounds, format); RectangleF[] rectsF = new RectangleF[regions.Length]; for (int RFx = 0; RFx < regions.Length; RFx++) rectsF[RFx] = regions[RFx].GetBounds(e.Graphics); e.Graphics.FillRectangles(Brushes.LightGreen, rectsF); } TextRenderer.DrawText(e.Graphics, ItemString, e.Font, bounds, e.ForeColor, flags); } 

Graphics.DrawString()实现

 private void listBox1_DrawItem(object sender, DrawItemEventArgs e) { e.DrawBackground(); Rectangle bounds = new Rectangle(e.Bounds.X - GraphicsPaddingOffset, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height); string ItemString = listBox1.GetItemText(listBox1.Items[e.Index]); List TheIndexList = Regex.Matches(ItemString, HighLightString) .Cast() .Select(s => s.Index).ToList(); StringFormat format = new StringFormat(StringFormat.GenericDefault); if (TheIndexList.Count > 0) { CharacterRange[] CharRanges = new CharacterRange[TheIndexList.Count]; for (int CharX = 0; CharX < TheIndexList.Count; CharX++) CharRanges[CharX] = new CharacterRange(TheIndexList[CharX], HighLightString.Length); format.SetMeasurableCharacterRanges(CharRanges); Region[] regions = e.Graphics.MeasureCharacterRanges(ItemString, e.Font, e.Bounds, format); RectangleF[] rectsF = new RectangleF[regions.Length]; for (int RFx = 0; RFx < regions.Length; RFx++) rectsF[RFx] = regions[RFx].GetBounds(e.Graphics); e.Graphics.FillRectangles(Brushes.LightGreen, rectsF); } using (SolidBrush brush = new SolidBrush(e.ForeColor)) e.Graphics.DrawString(ItemString, e.Font, brush, bounds, format); } 

注意:
根据ListBox.DrawMode ,可能需要订阅ListBox.MeasureItem()事件或将.ItemHeight属性设置为.ItemHeight值。

 private void listBox1_MeasureItem(object sender, MeasureItemEventArgs e) { e.ItemHeight = listBox1.Font.Height; }