Thursday, December 17, 2009

Textblock Inlines - Accepting Custom Markup As Content

If you've ever used Textblocks in your Silverlight and WPF applications, i'm sure you've initially found some of the same kinds of limitations that i have with formatting the text of the control.

There are built in ways around these limitations. I'm going to show you how to dynamically add content to your silverlight or wpf app's Textblock while giving it formatting specific to your custom markup!

First we'll start by adding content to our XML file!

<?xml version="1.0" encoding="utf-8" ?>
<MyData>
  <MainPageText>
    Sometimes in life, you take a step back and think to yourself <i>"Wow, i really lucked out"</i>. <br /><br />That's how i feel about my girlfriend <b>Ashley</b>. She is amazing and i'm <u>definitely</u> going to ask her to marry me!
  </MainPageText>
</MyData>

I'm not going to cover the loading of the XML into your app, if you need help with that, you can go read my previous blog about Spawning Random Textblocks here.

Now we're going to make ourselves a little method to call from the codebehind
that will use our markup to format the textblock how we want it

public static void Format(TextBlock source, string value)
{    
  List<Run> afterBreaks = new List<Run>();
  foreach (string sp in Regex.Split(value, "<br />"))
  {
    Run beforeBreak = new Run();
    beforeBreak.Text = sp + '\n';
    afterBreaks.Add(beforeBreak);
  }

  List<Run> afterBold = new List<Run>();
  foreach (Run r in afterBreaks)
  {
    foreach (string sp in Regex.Split(r.Text, "</b>"))
    {
      string[] tempBold = Regex.Split(sp, "<b>");
      Run myRun = new Run();
      myRun.Text = tempBold[0];
      afterBold.Add(myrun);
      if (tempBold.Length > 1)
      {
        Run myBoldRun = new Run();
        myBoldRun.Text = tempbold[1];
        myBoldRun.FontWeight = FontWeights.ExtraBold;
        afterbold.Add(myBoldRun);
      }
    }
  }
            
  List<Run> afterItalics = new List<Run>();
  foreach (Run r in afterBold)
  {
    foreach (string sp in Regex.Split(r.Text, "</i>"))
    {
      string[] tempItalics = Regex.Split(sp, "<i>");
      Run myRun = new Run();
      myRun.Text = tempitalics[0];
      myRun.FontWeight = r.FontWeight;
      afterItalics.Add(myRun);
      if (tempitalics.Length > 1)
      {
        Run myItalicRun = new Run();
        myItalicRun.Text = tempItalics[1];
        myItalicRun.FontWeight = r.FontWeight;
        myItalicRun.FontStyle = FontStyles.Italic;
        afterItalics.Add(myItalicRun);
      }
    }
  }
  List<Run> afterUnderline = new List<Run>();
  foreach (Run r in afterItalics)
  {
    foreach (string sp in Regex.Split(r.Text, "</u>"))
    {
      string[] tempUnderline = Regex.Split(sp, "<u>");
      Run myRun = new Run();
      myRun.Text = tempUnderline[0];
      myRun.FontWeight = r.FontWeight;
      myRun.FontStyle = r.FontStyle;
      afterUnderline.Add(myRun);
      if (tempUnderline.Length > 1)
      {
        Run myUnderlinedRun = new Run();
        myUnderlinedRun.Text = tempUnderline[1];
        myUnderlinedRun.FontWeight = r.FontWeight;
        myUnderlinedRun.FontStyle = r.FontStyle;
        myUnderlinedRun.TextDecorations = TextDecorations.Underline;
        afterUnderline.Add(myUnderlinedRun);
      }
    }
  }
  foreach (Run r in afterUnderline) source.Inlines.Add(r);
}

Usage for this is easy, you pass it two parameters, first being the Textblock we are going to put the formatted text into and second being the string of text that has the markup that needs formatting!

Format(textBlock1,myvaluefromXML);

You can modify the function to accept and format more HTML-like values, or just use it as is! I have attached my class file below that you can easily add to your project ready for use!

TextFormatting.cs