Thursday, October 29, 2009

Text Wrap Method

Although Graphics.DrawString() will wrap text if you give it a rectangle, it wont tell you the resulting height once its done. This makes it really difficult to resize the texts container or add a scrollbar.

My solution to this problem is a simple text wrap method, well two methods which work really well on a string which is all the same font. So far I haven't needed to work with any display strings more complicated than that.

The text wrap is split into two functions, the first takes a string and splits it on spaces. Then it creates lines from the string that fit within the given width restriction. The second simply wraps this and splits the original string by the new line character, passing in the resulting strings and adding the new results to its list of lines.


// Using the given graphics object and width restriction the provided
// string is split into lines

private List SplitLines(Graphics g, string text, int pWidth)
{
   string[] lines = text.Split(new string[] { "\r\n" }, StringSplitOptions.None);
   List items = new List();
   foreach (string line in lines)
   {
      items.AddRange(this.SplitWords(g, line, pWidth, ref y));
   }
   return items;
}


The above method is the easy one that just puts the results together. Bellow is the method that will workout which line each word in the string should be on.


// This will split the given string into lines
// no longer then the provided width

private List SplitWords(Graphics g, string text, int pWidth)
{
   string[] items = text.Split(' ');
   float width = 0;
   string renderString = "";
   List renderList = new List();
   for (int i = 0; i < items.Length; i++)
   {
      SizeF stringLength = g.MeasureString(items[i], this.Font);
      if (width + stringLength.Width < pWidth)
      {
         // not too long yet
         if (renderString != "")
         renderString += " ";
         renderString += items[i];
         width += stringLength.Width;
      }
      else
      {
         // this will make it too long, render the current string
         // and start a new line

         renderList.Add(renderString);
         renderString = items[i];
         width = stringLength.Width;
      }
   }
   if (renderString != "")
   {
      // add any left over to the list
      renderList.Add(renderString);
   }
   return renderList;
}


Then to draw you just need to do a loop in the paint method and draw each line. When combined with the flicker free code posted before, this is really useful for large text display.

Later I will post the full code for a ScrollableLabel control which I find much more useful then a label, and better looking then a textbox.

No comments:

Post a Comment