2017-02-21 3 views
1

Recherche de la largeur de chaîne en C# pour simuler l'enveloppe de texte et la position du texte (maintenant écrit dans richTextBox).Comportement étrange lors du calcul de la largeur de chaîne en pixels pour la simulation d'enveloppement de mots

La taille de richTextBox est 555x454 px et j'utilise la police à espacement fixe Courier New 12pt. J'ai essayé TextRenderer.MeasureText() et Graphics.MeasureString() méthodes.

TextRenderer retournait des valeurs plus grandes que Graphics alors le texte qui correspond normalement à une ligne, mon code déterminé devrait être enveloppé à l'autre ligne. En revanche, en utilisant Graphics, mon code a déterminé qu'une chaîne particulière est plus courte que celle imprimée dans l'original richTextBox, donc elle a été enroulée à la ligne suivante au mauvais endroit. Pendant le débogage, j'ai découvert que les largeurs calculées diffèrent, ce qui est étrange car j'utilise une police à espacement fixe, donc les largeurs devraient être les mêmes pour tous les caractères. Mais je reçois quelque chose comme ça de Graphics.MeasureString() (exemple: '' - 5.33333254, 'S' - 15.2239571, '\ r' - 5.328125). Comment puis-je calculer avec précision la largeur de chaîne avec C# et simuler ainsi le retour à la ligne et déterminer des positions de texte particulières en pixels?

Pourquoi la largeur diffère-t-elle en caractères différents lors de l'utilisation d'une police monopolaire? Remarque: Je travaille sur un projet personnel de suivi des yeux et je veux déterminer où des morceaux de texte particuliers ont été placés pendant l'expérience afin que je puisse dire sur quels mots l'utilisateur regardait. Par ex. à l'heure t l'utilisateur cherchait sur le point [256,350] px et je sais qu'à cet endroit il y a appel de la méthode WriteLine. Mon stimulus visuel cible est le code source, avec des retraits, des tabulations, des fins de ligne, placés dans une zone de texte modifiable (dans le futur peut-être un simple éditeur de code source en ligne).

Voici mon code:

//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; 
     } 

Répondre

0

J'ai finalement ajouté quelques éléments dans mon code. Je vais le raccourcir.

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; 
    } 

Avec ces corrections, j'obtiens la bonne largeur de caractères communs (en utilisant une police à espacement constant j'ai des largeurs égales).

Mais il y a encore des problèmes avec d'autres comme \t et espaces blancs \n où je reçois 0.0078125 et 9.6015625 lorsque la largeur de mesure avec Courier New, police 12pt. La deuxième valeur est une largeur de n'importe quel caractère tapé avec cette police ainsi ce n'est pas un gros problème mais il serait mieux d'être 0 ou ai-je tort? Si quelqu'un a une suggestion pour résoudre ce problème, laissez un commentaire s'il vous plaît.

0

Je crois qu'il est beaucoup plus facile d'accomplir votre tâche en obtenant un caractère dans un endroit donné sur un écran. Par exemple, si vous utilisez le contrôle RichTextBox, reportez-vous à la méthode RichTextBox.GetCharIndexFromPosition pour obtenir l'index du caractère le plus proche de l'emplacement spécifié. Voici un exemple de code qui illustre l'idée:

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

C'est plus difficile. RichTextBox est juste à des fins de test.Et d'ailleurs je le garde séparé. Il y aura une application (probablement un éditeur de code source en ligne - mais maintenant simple RichTextBox), et une autre application séparée qui traitera les données hors ligne rassemblées dans la première application. – Gondil

+1

Oui, je comprends que c'est compliqué, mais pour autant que je comprenne votre objectif final, comme vous le dites, "afin que je puisse dire sur quels mots était l'utilisateur à la recherche". Sur une page Web, une approche avec le calcul de la taille du texte et la simulation d'un mot d'emballage est beaucoup plus compliquée en raison de différentes résolutions d'écran, différents navigateurs, différents paramètres de zoom et ainsi de suite. C'est pourquoi je pense qu'il est plus facile d'obtenir du texte en utilisant un certain point sur un écran. Pour le web cette approche a été discutée ici: http://stackoverflow.com/questions/2444430/how-to-get-a-word-under-cursor-using-javascript – Pasick