web-dev-qa-db-fra.com

Word envelopper une chaîne sur plusieurs lignes

J'essaye de transformer une chaîne en plusieurs lignes. Chaque ligne aura une largeur définie.

Par exemple, j'obtiendrais ce résultat si je le réduis à une zone de 120 pixels de large.

Lorem ipsum dolor sit amet,
consectetur elipiscing elit. Sed augue
velit, sit amet tempor non vulputate,
dictum vitae lacus. In vitae ante
justo, ut accumsan sem. Donec
pulvinar, nisi nec sagittis conséquat,
sem orci luctus velit, sed elementum
ligula ante nec neque. Pellentesque
habitant morbi tristique senectus et
netus et malesuada fames ac turpis
egestas. Etiam erat est, pellentesque
eget tincidunt ut, egestas in ante.
Nulla vitae vulputate velit. Proin dans
congue neque. Cras rutrum sodales
sapien, ut convallis erat auctor vel.
Duis ultricies pharetra dui, sagittis
varius mauris tristique a. Nam ut
neque id risus tempor hendrerit.
Maecenas ut lacus nunc. Nulla
fermentum ornare rhoncus. Nulla
gravida vestibulum odio, vel commodo
magna condimentum quis. Quisque
sollicitudin blandit mi, non varius
libero lobortis eu. Vestibule eu
turpis massa, id tincidunt orci.
Curabitur pellentesque urna non risus
adipiscing facilisis. Mauris vel
accumsan purus. Proin quis enim nca
sem tempor vestibulum ac vitae augue. 

35
ForrestWhy
static void Main(string[] args)
{
    List<string> lines = WrapText("Add some text", 300, "Calibri", 11);

    foreach (var item in lines)
    {
        Console.WriteLine(item);
    }

    Console.ReadLine();
}

static List<string> WrapText(string text, double pixels, string fontFamily, 
    float emSize)
{
    string[] originalLines = text.Split(new string[] { " " }, 
        StringSplitOptions.None);

    List<string> wrappedLines = new List<string>();

    StringBuilder actualLine = new StringBuilder();
    double actualWidth = 0;

    foreach (var item in originalLines)
    {
        FormattedText formatted = new FormattedText(item, 
            CultureInfo.CurrentCulture, 
            System.Windows.FlowDirection.LeftToRight,
            new Typeface(fontFamily), emSize, Brushes.Black);

        actualLine.Append(item + " ");
        actualWidth += formatted.Width;

        if (actualWidth > pixels)
        {
            wrappedLines.Add(actualLine.ToString());
            actualLine.Clear();
            actualWidth = 0;
        }
    }

    if(actualLine.Length > 0)
        wrappedLines.Add(actualLine.ToString());

    return wrappedLines;
}

Ajoutez les bibliothèques WindowsBase et PresentationCore.

38
as-cii

Le code suivant, tiré de ce blogpost , vous aidera à faire votre travail.

Vous pouvez l'utiliser de cette façon:

string wordWrappedText = WordWrap( <yourtext>, 120 );

Veuillez noter que le code n'est pas le mien, je ne fais ici que rapporter la fonction principale de votre produit. 

protected const string _newline = "\r\n";

public static string WordWrap( string the_string, int width ) {
    int pos, next;
    StringBuilder sb = new StringBuilder();

    // Lucidity check
    if ( width < 1 )
        return the_string;

    // Parse each line of text
    for ( pos = 0; pos < the_string.Length; pos = next ) {
        // Find end of line
        int eol = the_string.IndexOf( _newline, pos );

        if ( eol == -1 )
            next = eol = the_string.Length;
        else
            next = eol + _newline.Length;

        // Copy this line of text, breaking into smaller lines as needed
        if ( eol > pos ) {
            do {
                int len = eol - pos;

                if ( len > width )
                    len = BreakLine( the_string, pos, width );

                sb.Append( the_string, pos, len );
                sb.Append( _newline );

                // Trim whitespace following break
                pos += len;

                while ( pos < eol && Char.IsWhiteSpace( the_string[pos] ) )
                    pos++;

            } while ( eol > pos );
        } else sb.Append( _newline ); // Empty line
    }

    return sb.ToString();
}

/// <summary>
/// Locates position to break the given line so as to avoid
/// breaking words.
/// </summary>
/// <param name="text">String that contains line of text</param>
/// <param name="pos">Index where line of text starts</param>
/// <param name="max">Maximum line length</param>
/// <returns>The modified line length</returns>
public static int BreakLine(string text, int pos, int max)
{
  // Find last whitespace in line
  int i = max - 1;
  while (i >= 0 && !Char.IsWhiteSpace(text[pos + i]))
    i--;
  if (i < 0)
    return max; // No whitespace found; break at maximum length
  // Find start of whitespace
  while (i >= 0 && Char.IsWhiteSpace(text[pos + i]))
    i--;
  // Return length of text before whitespace
  return i + 1;
}
19
Lorenzo

Voici une version que j'ai conçue pour mon jeu XNA ...

(Notez que c'est un extrait, pas une définition de classe appropriée. Profitez-en!)

using System;
using System.Text;
using Microsoft.Xna.Framework.Graphics;

public static float StringWidth(SpriteFont font, string text)
{
    return font.MeasureString(text).X;
}

public static string WrapText(SpriteFont font, string text, float lineWidth)
{
    const string space = " ";
    string[] words = text.Split(new string[] { space }, StringSplitOptions.None);
    float spaceWidth = StringWidth(font, space),
        spaceLeft = lineWidth,
        wordWidth;
    StringBuilder result = new StringBuilder();

    foreach (string Word in words)
    {
        wordWidth = StringWidth(font, Word);
        if (wordWidth + spaceWidth > spaceLeft)
        {
            result.AppendLine();
            spaceLeft = lineWidth - wordWidth;
        }
        else
        {
            spaceLeft -= (wordWidth + spaceWidth);
        }
        result.Append(Word + space);
    }

    return result.ToString();
}
5
tvwxyz

Merci! Je prends metod de as-cii answer avec quelques modifications, pour l’utiliser dans les formulaires Windows. Utiliser TextRenderer.MeasureText au lieu de FormattedText

static List<string> WrapText(string text, double pixels, Font font)
{
string[] originalLines = text.Split(new string[] { " " }, 
    StringSplitOptions.None);

List<string> wrappedLines = new List<string>();

StringBuilder actualLine = new StringBuilder();
double actualWidth = 0;

foreach (var item in originalLines)
{
    int w = TextRenderer.MeasureText(item + " ", font).Width;
    actualWidth += w;

    if (actualWidth > pixels)
    {
        wrappedLines.Add(actualLine.ToString());
        actualLine.Clear();
        actualWidth = w;
    }

    actualLine.Append(item + " ");
}

if(actualLine.Length > 0)
    wrappedLines.Add(actualLine.ToString());

return wrappedLines;
}

Et une petite remarque: la ligne actualLine.Append (item + ""); doit être placé après la vérification de la largeur, car si largeur réelle> pixels, ce mot doit figurer à la ligne suivante.

3
undejavue

Pour Winforms:

List<string> WrapText(string text, int maxWidthInPixels, Font font)
{
    string[] originalLines = text.Split(new string[] { " " }, StringSplitOptions.None);

    List<string> wrappedLines = new List<string>();

    StringBuilder actualLine = new StringBuilder();
    int actualWidth = 0;

    foreach (var item in originalLines)
    {
        Size szText = TextRenderer.MeasureText(item, font);

        actualLine.Append(item + " ");
        actualWidth += szText.Width;

        if (actualWidth > maxWidthInPixels)
        {
            wrappedLines.Add(actualLine.ToString());
            actualLine.Clear();
            actualWidth = 0;
        }
    }

    if (actualLine.Length > 0)
        wrappedLines.Add(actualLine.ToString());

    return wrappedLines;
}
1
Martin.Martinsson
public static string GetTextWithNewLines(string value = "", int charactersToWrapAt = 35, int maxLength = 250)
        {
            if (string.IsNullOrWhiteSpace(value)) return "";

            value = value.Replace("  ", " ");
            var words = value.Split(' ');
            var sb = new StringBuilder();
            var currString = new StringBuilder();

            foreach (var Word in words)
            {
                if (currString.Length + Word.Length + 1 < charactersToWrapAt) // The + 1 accounts for spaces
                {
                    sb.AppendFormat(" {0}", Word);
                    currString.AppendFormat(" {0}", Word);
                }
                else
                {
                    currString.Clear();
                    sb.AppendFormat("{0}{1}", Environment.NewLine, Word);
                    currString.AppendFormat(" {0}", Word);
                }
            }

            if (sb.Length > maxLength)
            {
                return sb.ToString().Substring(0, maxLength) + " ...";
            }

            return sb.ToString().TrimStart().TrimEnd();
        }
0
theJerm

Je voulais envelopper le texte pour le dessiner ensuite dans mon image. J'ai essayé la réponse de @ as-cii, mais cela n'a pas fonctionné comme prévu. Il étend toujours la largeur donnée de ma ligne (peut-être parce que je l'utilise en combinaison avec un objet Graphics pour dessiner le texte dans mon image). De plus, sa réponse (et les réponses connexes) ne fonctionne que pour> .Net 4 frameworks. Dans le framework .Net 3.5, il n'y a pas de fonction Clear () pour StringBuilder objects. Alors voici une version éditée:

    public static List<string> WrapText(string text, double pixels, string fontFamily, float emSize)
    {
        string[] originalWords = text.Split(new string[] { " " },
            StringSplitOptions.None);

        List<string> wrappedLines = new List<string>();

        StringBuilder actualLine = new StringBuilder();
        double actualWidth = 0;

        foreach (string Word in originalWords)
        {
            string wordWithSpace = Word + " ";
            FormattedText formattedWord = new FormattedText(wordWithSpace,
                CultureInfo.CurrentCulture,
                System.Windows.FlowDirection.LeftToRight,
                new Typeface(fontFamily), emSize, System.Windows.Media.Brushes.Black);

            actualLine.Append(wordWithSpace);
            actualWidth += formattedWord.Width;

            if (actualWidth > pixels)
            {
                actualLine.Remove(actualLine.Length - wordWithSpace.Length, wordWithSpace.Length);
                wrappedLines.Add(actualLine.ToString());
                actualLine = new StringBuilder();
                actualLine.Append(wordWithSpace);
                actualWidth = 0;
                actualWidth += formattedWord.Width;
            }
        }

        if (actualLine.Length > 0)
            wrappedLines.Add(actualLine.ToString());

        return wrappedLines;
    }

Parce que je travaille avec un objet Graphics, j’ai essayé la solution @Thorins. Cela a bien mieux fonctionné pour moi, car cela enveloppe bien mon texte. Mais j’ai fait quelques changements pour que vous puissiez donner à la méthode les paramètres requis. Il y avait aussi un bug: la dernière ligne n'a pas été ajoutée à la liste, lorsque la condition du bloc if dans la boucle for n'a pas été atteinte. Donc, vous devez ajouter cette ligne après. Le code édité ressemble à:

    public static List<string> WrapTextWithGraphics(Graphics g, string original, int width, Font font)
    {
        List<string> wrappedLines = new List<string>();

        string currentLine = string.Empty;

        for (int i = 0; i < original.Length; i++)
        {
            char currentChar = original[i];
            currentLine += currentChar;
            if (g.MeasureString(currentLine, font).Width > width)
            {
                // exceeded length, back up to last space
                int moveback = 0;
                while (currentChar != ' ')
                {
                    moveback++;
                    i--;
                    currentChar = original[i];
                }
                string lineToAdd = currentLine.Substring(0, currentLine.Length - moveback);
                wrappedLines.Add(lineToAdd);
                currentLine = string.Empty;
            }
        }

        if (currentLine.Length > 0)
            wrappedLines.Add(currentLine);

        return wrappedLines;
    }
0
user3806723

Vous pouvez obtenir la largeur (approximative) d'une chaîne de la classe System.Drawing.Graphics à l'aide de la méthode MeasureString (). Si vous avez besoin d'une largeur très précise, je pense que vous devez utiliser la méthode MeasureCharacterRanges (). Voici un exemple de code utilisant la méthode MeasureString () pour effectuer à peu près ce que vous avez demandé:

using System;
using System.Collections.Generic; // for List<>
using System.Drawing; // for Graphics and Font

private List<string> GetWordwrapped(string original)
{
    List<string> wordwrapped = new List<string>();

    Graphics graphics = Graphics.FromHwnd(this.Handle);
    Font font = new Font("Arial", 10);

    string currentLine = string.Empty;

    for (int i = 0; i < original.Length; i++)
    {
        char currentChar = original[i];
        currentLine += currentChar;
        if (graphics.MeasureString(currentLine, font).Width > 120)
        {
            // exceeded length, back up to last space
            int moveback = 0;
            while (currentChar != ' ')
            {
                moveback++;
                i--;
                currentChar = original[i];
            }
            string lineToAdd = currentLine.Substring(0, currentLine.Length - moveback);
            wordwrapped.Add(lineToAdd);
            currentLine = string.Empty;
        }
    }

    return wordwrapped;
}
0
Thorin