计算字符串宽度(以像素为单位)时的奇怪行为,以模拟自动换行

试图在C#中获取字符串宽度来模拟wordwrap和文本的位置(现在用richTextBox编写)。

richTextBox的大小是555×454像素,我使用等宽字体Courier New 12pt。

我尝试了TextRenderer.MeasureText()以及Graphics.MeasureString()方法。

TextRenderer返回的值大于Graphics所以文本通常适合一行,我的代码确定应该包装到其他行。

但是,另一方面,使用Graphics ,我的代码确定特定字符串比原始richTextBox中打印的要短,因此它被错误地包装到下一行。

在调试过程中,我发现计算出的宽度不同,这很奇怪,因为我使用的是等宽字体,所以所有字符的宽度应相同。 但是我从Graphics.MeasureString()得到类似的东西(例如:” – 5.33333254,’S’ – 15.2239571,’\ r’ – 5.328125)。

如何使用C#精确计算字符串宽度,从而模拟自动换行并确定特定的文本位置(以像素为单位)?

使用等宽字体时,为什么不同字符的宽度不同?

注意:我正在开展个人眼动追踪项目,我想确定在实验期间放置特定文本的部分,以便我可以判断用户正在查看哪些文字。 对于前者 在时间t用户正在查看点[256,350] px,我知道在这个地方有方法WriteLine调用。 我的目标视觉刺激是源代码,带有缩进,制表符,行结尾,放置在一些可编辑的文本区域(将来可能是一些简单的在线源代码编辑器)。

这是我的代码:

  //before method call var font = new Font("Courier New", 12, GraphicsUnit.Point); var graphics = this.CreateGraphics(); var wrapped = sourceCode.WordWrap(font, 555, graphics); public static List WordWrap(this string sourceCode, Font font, int width, Graphics g) { var wrappedText = new List(); // output var actualLine = new StringBuilder(); var actualWidth = 0.0f; // temp var for computing actual string length var lines = Regex.Split(sourceCode, @"(? !s.Equals("")).ToArray(); // split line by tabs and spaces and maintain delimiters separately foreach (string word in wordsOfLine) { var wordWidth = g.MeasureString(word, font).Width; // compute width of word if (actualWidth + wordWidth > width) // if actual line width is grather than width of text area { wrappedText.Add(actualLine.ToString()); // add line to list actualLine.Clear(); // clear StringBuilder actualWidth = 0; // zero actual line width } actualLine.Append(word); // add word to actual line actualWidth += wordWidth; // add word width to actual line width } if (actualLine.Length > 0) // if there is something in actual line add it to list { wrappedText.Add(actualLine.ToString()); } actualLine.Clear(); // clear vars actualWidth = 0; } return wrappedText; } 

我相信通过在屏幕上的给定位置下获取角色来完成任务要容易得多。 例如,如果您使用的是RichTextBox控件,请参阅RichTextBox.GetCharIndexFromPosition方法以获取最接近指定位置的字符的索引。 以下是一些演示该想法的示例代码:

 private void richTextBox1_MouseMove(object sender, MouseEventArgs e) { var textIndex = richTextBox1.GetCharIndexFromPosition(e.Location); if (richTextBox1.Text.Length > 0) label1.Text = richTextBox1.Text[textIndex].ToString(); } 

所以我最后在我的代码中添加了一些东西。 我会缩短它。

 public static List WordWrap(this string sourceCode, Font font, int width, Graphics g) { g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; var format = StringFormat.GenericTypographic; format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces; var width = g.MeasureString(word, font, 0, format).Width; } 

通过这些更正,我得到正确的常用字符宽度(使用等宽字体我得到相等的宽度)。

但是在使用Courier New,12pt字体测量宽度时,其他空格如\t\n仍有问题,我得到0.00781259.6015625 。 第二个值是用这个字体键入的任何字符的宽度,所以这不是一个大问题,但最好是0或我错了? 如果有人有解决这个问题的建议,请发表评论。