Wednesday, December 3, 2008

The TextBlock Inline property in Silverlight 2

I was testing with this dynamic conversion tool I've been working on when I ran into one of those moments of complete confusion about this regular expression I had to evaluate. The Regulator didn't provide me with any insight and responded differently from the actual .NET implementation (anyone any thoughts on how that is possible?).

To get a better grip on things, I decided to build a small tool for testing regular expressions myself and as I'm always looking for a good, or less then good, excuse for using Silverlight 2, I decided to use it as my UI. I figured it would be cool to have any matches in my input highlighted on the fly, so I dove into it with the following result:



So how does this relate to the TextBlock's inline property?
Well it is used twice in this application and it may not be as obvious to everyone.

Highlighting text in Silverlight without the use of any third party controls, is different from anything we're used to in either HTML, ASP.NET or Windows Forms. In HTML and ASP.NET it's quite obvious. In Windows Forms, one would simply use a selection to point out the text to change the font for that text, including the color.

In Silverlight 2, this is done trough the use of a TextBlock and it's Inline property. The Inline property is of the type InlineCollection, which is, offcourse, a collection of Inline objects. The Inline class in turn, is an abstract class that is inherited by two other classes, being Run and LineBreak. You would use Run to define a run of text and you would use LineBreak for it's obvious purpose of going to the next line.

I used this in XAML to define the tooltip you can see in the screenshot. It looks like this:

<TextBlock x:Name="matchesTextBlock">
<ToolTipService.ToolTip>
<ToolTip>
<TextBlock>
<Run>Matched text is colored red,</Run>
<LineBreak />
<Run>while unmatched text is colored black.</Run>
<LineBreak />
<Run>If the expression matches the entire</Run>
<LineBreak />
<Run>input, the background will color gray,</Run>
<LineBreak />
<Run>otherwise it will color white.</Run>
</TextBlock>
</ToolTip>
</ToolTipService.ToolTip>
</TextBlock>


As you see, once you get used to it, it isn't that hard. As you can aply a seperate style to each Inline element you can easily write some code to highlight the matches from a regular expression. The method that does it looks like this:


private void UpdateMathes()
{
string input = inputTextBox.Text;
string expression = expressionTextBox.Text;

MatchCollection matches = Regex.Matches(input, expression);

bool isMatch = matches.Count >= 1 && matches[0].Length == input.Length;

matchesTextBlock.Inlines.Clear();
int writtenChars = 0;
foreach (Match match in matches)
{
string inputToMatch = input.Substring(writtenChars, match.Index - writtenChars);
DrawUnmatchedText(inputToMatch);
writtenChars = match.Index + match.Length;
string matched = match.Value;
DrawMatchedText(matched);
}
string inputAfterMatch = input.Substring(writtenChars, input.Length - writtenChars);
DrawUnmatchedText(inputAfterMatch);

if (isMatch)
{
matchesBorder.Background = new SolidColorBrush(Colors.Gray);
}
else
{
matchesBorder.Background = new SolidColorBrush(Colors.White);
}
}

Some of you may have spotted the way that isMatched is filled and think that using Regex.IsMatch would be a more efficient way of doing this. Unfortunately Regex.IsMatch returns true if any match is found, so even a partial match would return true.

The two methods that do the actual work look like this:

private void DrawUnmatchedText(string text)
{
Run run = new Run();
run.Text = text;
run.Foreground = new SolidColorBrush(Colors.Black);
matchesTextBlock.Inlines.Add(run);
}

private void DrawMatchedText(string text)
{
Run run = new Run();
run.Text = text;
run.Foreground = new SolidColorBrush(Colors.Red);
matchesTextBlock.Inlines.Add(run);
}


So this is how you can easily format your text inside a textblock. As you may already know, I'm always keen to get your feedback on any of my articles and I'm always happy to answer any questions, so please leave your comments below.

UPDATE 31-03-2010: You can now download the original Silverlight 2 source from here. Thanks go out to alexander pointing out he was missing some source.

6 comments:

  1. This TextBlock inline insight helped me out, Thanks!

    ReplyDelete
  2. Nice, but please add the XAML that does the binding for the text highlighting.

    ReplyDelete
  3. Thanks for your comment, alexander. I've posted the complete source I used back then. Unfortunately it doesn't use databinding for the highlighted text. If you'd like to do that, check out the article: Highlighting databound information in Silverlight

    ReplyDelete
  4. Thanks for taking your time, i will look into that!

    ReplyDelete
  5. Exactly what I was looking for - the Inlines property is not adequately explained in the docs. Thanks!

    ReplyDelete