如何使用图形突出显示控件中的包装文本?
我需要使用fill rect突出显示控件中的特定字符。 我可以通过使用如下的Graphics.MeasureString()
方法获取文本的位置,如下所示,
var size = g.MeasureString(tempSearchText, style.Font, 0, StringFormat.GenericTypographic);
如果文本被包装,那么我无法找到该字符的确切边界以突出显示该文本。
我需要在包装的文本中获得给定字符的确切界限。 提供您的建议以实现此方案。
目前没有明确规定哪些控件,所以我测试了3种不同的控件:
TextBox
, RichTextbox
和ListBox
。
TextBox和RichTextbox具有相同的行为并共享相同的工具,因此无需定义两种不同的方法来实现相同的结果。
当然,RichTextbox提供了更多选项,包括RTF 。
另外,我正在测试Graphics.DrawString()
和TextRenderer.DrawText()
。
这是此测试的结果,因此更清楚代码的作用。
警告 :
对于此示例,我使用的是Control.CreateGraphics()
,因为TextBox
和RichTextBox
控件不提供Paint()
事件。 对于真实世界的应用程序,您应该创建一个派生自TextBox
或RichTextBox
的自定义控件并覆盖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()
设置为true
或false
似乎没有任何影响(至少在当前上下文中)。
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
以评估TextRenderer
和Graphics
在此处的行为是否不同。
存在一个小的差异:相对于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; }