Archive

Archive for March, 2009

Processing Multiline Text in a WPF TextBlock

March 23, 2009 1 comment

So I’m sure this will be filed in the *yawn* category for you experienced WPF developers out there, but I ran into an interesting item on Friday that I wanted to share.  I was developing a custom DialogBox that displays a lot of text and an image from a CLR object passed in from the calling Window.  The text is fairly long, three short paragraphs, but includes several bits of variable data (properties on the CLR object).

I had several options for how to go about this:

  1. Build the Text dynamically in the code behind.
  2. Create several TextBlock items and string them together, probably using WrapPanel, and bind the variables to the CLR properties.
  3. Store the desired text in the TextBlock.Text property with place holders and use it in a String.Format statement to insert the property values.  (I could have also stored this string in an external file)

I’m sure there are other options, but I didn’t want to spend a lot of time on this.  Option 1 just seemed too blunt, not to mention that I couldn’t see in Blend what it would look like from a design perspective.  Option 2 would probably work, and from a pure XAML approach would be preferable, but I was in a hurry so I went with what I knew (or so I thought) and chose option 3.

I immediately liked this idea because I could really see the end result in Blend.  To handle the variable embedding, I planned on reading in the TextBlock.Text value in the constructor and storing it in a private variable.  Then I could read the values passed in and reset the .Text property:

private string messageFormat ;
private DeleteImageDialogBox()
{
    this.InitializeComponent();
    messageFormat = MessageTextBlock.Text;
}

public DeleteImageDialogBox(PictureInfo pic, string archiveDirectory)
    : this()
{
    LayoutRoot.DataContext = pic;

    string message = String.Format(messageFormat,
        pic.GetRecordNumber(),
        pic.GetCardNumber(),
        archiveDirectory);

    MessageTextBlock.Text = message;
}

Unfortunately, this did not work.  A little debugging soon showed me why: TextBlock.Text was an empty string. This was confusing because Blend shows the Text property as containing my text.

Apparently, because I had CRLFs in my Text, the actual text was no longer exposed by the Text property, but was now part of the Inlines property.  Inlines is of type InlineCollection, a collection of Inline objects.  Inline objects are used to manage a line of content. Inline is an abstract class, so the actual collection can contain multiple types. In my case, I had to test for two types: LineBreak and Run.  In order to retain my formatting, if I detected a LineBreak I inserted “\r\n”.  If I detected a Run object, then I could grab the text.

So if you aren’t confused yet, or even if you are, here is the code I used to extract my full text into a usable string variable:

StringBuilder s = new StringBuilder();
foreach (var line in MessageTextBlock.Inlines)
{
    if (line is LineBreak)
    {
        s.Append("\r\n");
    }
    else if (line is Run)
    {
        Run text = (Run)line;
        s.Append(text.Text);
    }
}
messageFormat = s.ToString();

The only formatting I am retaining here are the LineBreaks: if my text had Underlines, Bolds, etc., I would need to add more code above to retain them.  This may seem like a lot of effort, but if I can definitely see the value for text formatting and processing.  I expect to see this again when I get deeper into WPF printing.

Categories: WPF