This is tricky.
For now it's only a part of the solution but here is an example to highlight a search in all Textblocks of in the VisualTree of a root object.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
public class GlobalTextHighlighter
{
public static Brush GetHighlightBackground(DependencyObject obj)
{
return (Brush)obj.GetValue(HighlightBackgroundProperty);
}
public static void SetHighlightBackground(DependencyObject obj, Brush value)
{
obj.SetValue(HighlightBackgroundProperty, value);
}
// Using a DependencyProperty as the backing store for HighlightBackground. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HighlightBackgroundProperty =
DependencyProperty.RegisterAttached("HighlightBackground", typeof(Brush), typeof(GlobalTextHighlighter), new PropertyMetadata(Brushes.Yellow, new PropertyChangedCallback(RefreshHighlighting)));
public static Brush GetHighlightForeground(DependencyObject obj)
{
return (Brush)obj.GetValue(HighlightForegroundProperty);
}
public static void SetHighlightForeground(DependencyObject obj, Brush value)
{
obj.SetValue(HighlightForegroundProperty, value);
}
// Using a DependencyProperty as the backing store for HighlightForeground. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HighlightForegroundProperty =
DependencyProperty.RegisterAttached("HighlightForeground", typeof(Brush), typeof(GlobalTextHighlighter), new PropertyMetadata(Brushes.Black, new PropertyChangedCallback(RefreshHighlighting)));
public static string GetSearchText(DependencyObject obj)
{
return (string)obj.GetValue(SearchTextProperty);
}
public static void SetSearchText(DependencyObject obj, string value)
{
obj.SetValue(SearchTextProperty, value);
}
// Using a DependencyProperty as the backing store for SearchText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SearchTextProperty =
DependencyProperty.RegisterAttached("SearchText", typeof(string), typeof(GlobalTextHighlighter), new PropertyMetadata(string.Empty, new PropertyChangedCallback(RefreshHighlighting)));
private static void RefreshHighlighting(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(d != null)
{
try
{
Brush background = (Brush)d.GetValue(HighlightBackgroundProperty);
Brush foreground = (Brush)d.GetValue(HighlightForegroundProperty);
string highlightText = (string)d.GetValue(SearchTextProperty);
FindVisualChildren<TextBlock>(d)
.ToList()
.ForEach(textBlock =>
{
try
{
string text = textBlock.Text;
if (!string.IsNullOrEmpty(text))
{
if (string.IsNullOrEmpty(highlightText))
{
textBlock.Text = text;
}
else
{
int index = text.IndexOf(highlightText, StringComparison.CurrentCultureIgnoreCase);
if (index < 0)
textBlock.Text = text;
else
textBlock.Inlines.Clear();
while (index >= 0)
{
textBlock.Inlines.AddRange(new Inline[]
{
new Run(text.Substring(0, index)),
new Run(text.Substring(index, highlightText.Length))
{
Background = background,
Foreground = foreground
}
});
text = text.Substring(index + highlightText.Length);
index = text.IndexOf(highlightText, StringComparison.CurrentCultureIgnoreCase);
if (index < 0)
{
textBlock.Inlines.Add(new Run(text));
}
}
}
}
}
catch
{ }
});
}
catch { }
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
}
And you can use it on the root object from where you want to search.
Example :
<TextBox x:Name="GlobalSearchTextBox"
Text="{Binding GlobalSearch, UpdateSourceTrigger=PropertyChanged, Delay=100}"/>
<DockPanel local:GlobalTextHighlighter.SearchText="{Binding GlobalSearch}"
local:GlobalTextHighlighter.HighlightBackground="Orange"
local:GlobalTextHighlighter.HighlightForeground="Blue">
<!-- ... -->
</DockPanel>
It's based on WPF TextBlock Highlighter and Find all controls in WPF Window by type.
As I said it's not yet a complete solution.
And it comes with some drawbacks.
- It doesn't work with all components that use AccessText like Labels.
- It doesn't work with editing components like TextBoxs or RichTextBox.
- It's not browsing from one match to an other.
- If you want that you need to manage nested ScrollAreas.
I hope it already helps to approach the solution.