Processing Multiline Text in a WPF TextBlock
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:
- Build the Text dynamically in the code behind.
- Create several TextBlock items and string them together, probably using WrapPanel, and bind the variables to the CLR properties.
- 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.
oke… thanks for share information…