I was doing some updates to a debugging tool for WPF... I was adding a filter feature that would highlight the characters in yellow as you entered them.
I was originally using a TextBlock to bind to by information and that worked fine until I was adding the rich text feature of highlighting the search string. You can't bind to the Text property of a TextBlock and pass in a Inline object (shown below, which is what supports the rich text within a TextBlock).
The only time you could pass Inlines to the TextBlock was in the constructor... that wasn't going to work with a binding.
That forced me down the road of investigating the use of a Label, as I could bind to the Content property and it would render the Inline elements I was creating just fine... this resulted in the rich text highlighting. However, there was a couple of issues... since this is constantly taking information from the Win32 OutPutDebugString call (see DbgMon) it really needed to perform well when rendering. I was noticing a little bit of a performance hit... not substantial, but noticeable (maybe that is substantial then). In addition, wrapping text with in a Label turned out to be not so easy.
So I went back to the TextBlock. I tried to bind to the Inlines property. There was a couple of issues with that... one it wasn't a DependencyProperty and the other issue was that it was read-only.
Solution:
Since I had written a converter to handle highlighting of the filtered text within the string, I was returning Inlines. I modified the convert so that I would return a TextBlock and since I was creating the inlines I was able to pass them into the constructor of the TextBlock. Instead of binding to a TextBlock in the DataTemplate, I binded to a ContentControl, which would intern using the converter to get the TextBlock that was generated with the filter text highlighted.
The resulting converter looked like the following:
public class StringToHighlightConverter : IValueConverter
{
public object Convert(object values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
//get the data found in the text
string dataText = (string)values;
if (!string.IsNullOrEmpty(dataText) && !string.IsNullOrEmpty(TextSearchFilter.SearchClause))
List<StringOccurance> highlightedWords = new List<StringOccurance>();
foreach(string token in TextSearchFilter.TokenizedSearchClause)
highlightedWords.AddRange(StringEx.FindStringOccurances(dataText, token));
}
if (highlightedWords.Count == 0)
return CreateTextBlock(dataText);
int textStartIdx = 0;
string plainWord = string.Empty;
int wordOccuranceIdx = 0; //keep count of the words we have proccessed
Span phrase = new Span();
for (int i = textStartIdx; i < dataText.Length; i++)
if (wordOccuranceIdx == highlightedWords.Count)
//We have reached the maximum word occurances, add the plain word span
Span wordSpan = CreateWordSpan(dataText.Substring(i), false);
phrase.Inlines.Add(wordSpan);
break;
//get the currrent word that needs to be highlighted
StringOccurance wordOccurance = highlightedWords[wordOccuranceIdx];
if (wordOccurance.StartIndex == i)
//add the plain word span
if (!string.IsNullOrEmpty(plainWord))
Span wordSpan = CreateWordSpan(plainWord, false);
plainWord = string.Empty;
Span highLightedWordSpan = CreateWordSpan(wordOccurance.Value, true);
wordOccuranceIdx++;
//issue with this
i = i + (wordOccurance.Value.Length - 1);
phrase.Inlines.Add(highLightedWordSpan);
continue;
if (i >= dataText.Length)
//Span wordSpan = CreateWordSpan(dataText.Substring(i), false);
plainWord = plainWord + dataText[i];
return CreateTextBlock(phrase);
private TextBlock CreateTextBlock(Inline text)
TextBlock block = new TextBlock(text);
block.TextWrapping = System.Windows.TextWrapping.Wrap;
return block;
private TextBlock CreateTextBlock(string text)
Span textSpan = new Span();
textSpan.Inlines.Add(text);
return CreateTextBlock(textSpan);
private Span CreateWordSpan(string text, bool highlight)
Span wordSpan = new Span();
if( highlight == true)
wordSpan.Background = Brushes.Yellow;
wordSpan.Inlines.Add(text);
return wordSpan;
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
throw new NotSupportedException("Not supported");
The xaml in the DataTemplate looked like:
<DataTemplate>
<ContentControl Content="{Binding OutPutDebugString,
Converter={StaticResource highlightConverter}}"
VerticalAlignment="Top" />
</DataTemplate>
Posted in Code | Development | WPF |Comments [563]