2017-02-21 110 views
1

尝试在C#中获取字符串宽度以模拟wordwrap和文本的位置(现在写在richTextBox中)。计算字符串宽度的奇怪行为(以像素为单位)以模拟文字换行

richTextBox的大小是555x454像素,我使用等宽字体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<string> WordWrap(this string sourceCode, Font font, int width, Graphics g) 
     { 
      var wrappedText = new List<string>(); // output 
      var actualLine = new StringBuilder(); 
      var actualWidth = 0.0f; // temp var for computing actual string length 
      var lines = Regex.Split(sourceCode, @"(?<=\r\n)"); // split input to lines and maintain line ending \r\n where they are 
      string[] wordsOfLine; 

      foreach (var line in lines) 
      { 
       wordsOfLine = Regex.Split(line, @"(|\t)").Where(s => !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; 
     } 

回答

0

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

public static List<string> 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; 
    } 

有了这个更正我得到正确的普通字符宽度(使用等宽字体我得到相等的宽度)。

但仍有其它空格像\t\n用宋体,,字型测量宽度的时候,我得到0.00781259.6015625问题。第二个值是用这个字体输入的任何字符的宽度,所以它不是一个大问题,但是最好是0还是我错了?如果有人有解决此问题的建议,请发表评论。

0

我认为,这是很容易通过在屏幕上给定位置获得一个字符来完成你的任务。例如,如果您使用的是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(); 
} 
+0

这是相当困难的。 RichTextBox仅用于测试目的。而且我保持分离。将有一个应用程序(可能是一些在线源代码编辑器 - 但现在只是简单的RichTextBox)和另一个分离的应用程序,它将处理在第一个应用程序中收集的离线数据。 – Gondil

+1

是的,我知道这很复杂,但就我所理解的最终目标而言,“所以我可以告诉用户看哪些单词”。在网页上,由于不同的屏幕分辨率,不同的浏览器,不同的缩放设置等等,计算文本大小和模拟文字换行的方法会变得更加复杂。这就是为什么我认为通过使用屏幕上的某个点来获取文本更为容易。对于网络这种方法在这里讨论:http://stackoverflow.com/questions/2444430/how-to-get-a-word-under-cursor-using-javascript – Pasick

相关问题