【问题标题】:Word wrap a string in multiple linesWord 将字符串换行成多行
【发布时间】:2010-10-18 16:43:18
【问题描述】:

我正在尝试将字符串换行成多行。每条线都有一个定义的宽度。

例如,如果我将它换行到宽度为 120 像素的区域,我会得到这个结果:

Lorem ipsum dolor sit amet,
consectetur adipiscing 精英。 Sed 奥格
velit, tempor non vulputate sat amet,
格言 vitae lacus。履历
justo, ut accumsan sem。多内克
pulvinar, nisi nec sagittis consequat,
sem orci luctus velit, sed elementum
前额叶舌。佩伦特斯克
居民 morbi tristique senectus et
netus et malesuada 成名 ac turpis
埃斯塔斯。 Etiam erat est, pellentesque
eget tincidunt ut,前注中的 egestas。
Nulla vitae vulputate velit。普罗因
congue neque。 Cras rut​​rum sodales
sapien, ut convallis erat auctor vel.
Duis ultricies pharetra dui, sagittis
varius mauris tristique a.南乌
neque id risus tempor hendrerit。
Maecenas ut lacus nunc。纳拉
酵母菌。纳拉
怀孕前庭 odio, vel commodo
magna condimentum quis。奎斯克
sollicitudin blandit mi, non varius
libero lobortis 欧盟。前庭欧盟
turpis massa, id tincidunt orci。
Curabitur pellentesque urna non risus
脂肪促进作用。毛里斯维尔
accumsan purus。 Proin quis enim nec
sem tempor 前庭 ac vitae augue。

【问题讨论】:

  • 您希望结果如何?一个字符串数组?还是打印到位图的字符串?你用什么字体和大小?
  • 字符串数组应该不错!
  • 不要忘记考虑如果您的文本没有 120 像素的空格该怎么办。

标签: c# word-wrap


【解决方案1】:
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;
}

添加WindowsBasePresentationCore 库。

【讨论】:

  • 有经验吗?FormattedText 的测量值与 WinForms 文本的呈现有多少匹配(使用Graphics.DrawStringTextRenderer.DrawText)?
【解决方案2】:

这是我为XNA 游戏设计的版本...

(请注意,这是一个 sn-p,不是正确的类定义。享受吧!)

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

【讨论】:

    【解决方案3】:

    谢谢!我采用as-cii's answer 的方法进行了一些更改,以便在 Windows 窗体中使用它。我正在使用 TextRenderer.MeasureText 而不是 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;
    }
    

    还有一点说明:actualLine.Append(item + " ");这行需要在检查宽度后放置,因为如果actualWidth>像素,这个词必须在下一行.

    【讨论】:

      【解决方案4】:
      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();
      }
      

      【讨论】:

        【解决方案5】:

        对于Windows Forms

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

        【讨论】:

          【解决方案6】:

          您可以使用 MeasureString() 方法从 System.Drawing.Graphics 类获取字符串的(近似)宽度。如果您需要非常精确的宽度,我认为您必须使用 MeasureCharacterRanges() 方法。下面是一些示例代码,使用 MeasureString() 方法大致完成您的要求:

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

          【讨论】:

            【解决方案7】:

            我想包装文本,然后在我的图像中绘制它。我试过as-cii's answer,但它在我的情况下没有按预期工作。它总是扩展我的线条的给定宽度(可能是因为我将它与 Graphics 对象结合使用来在我的图像中绘制文本)。

            此外,他的答案(和相关答案)仅适用于 > .NET 4 框架。在框架 .NET 3.5 中,StringBuilder 对象没有任何 Clear() 函数。所以这里是一个经过编辑的版本:

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

            因为我正在使用 Graphics 对象,所以我尝试了@Thorins 解决方案。这对我来说效果更好,因为它正确地包装了我的文本。但是我做了一些更改,以便您可以为该方法提供所需的参数。还有一个错误:当未达到 for 循环中 if 块的条件时,最后一行没有添加到列表中。所以你必须在之后添加这一行。编辑后的代码如下:

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

            【讨论】:

            • Re "isn't any Clear() function":可以简单地将StringBuilder对象的属性“Length”设置为0来清除文本内容(同时保持当前容量)。 ExamplemScratchSB.Length = 0;。它对于重用临时字符串缓冲区很有用(避免创建开销和内存压力)。
            猜你喜欢
            • 2021-10-08
            • 1970-01-01
            • 2015-09-18
            • 1970-01-01
            • 2013-10-23
            • 2023-03-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多