diff --git a/src/Shared/HandyControlDemo_Shared/Data/Model/DemoItemModel.cs b/src/Shared/HandyControlDemo_Shared/Data/Model/DemoItemModel.cs
index b0811427..3cbaab9c 100644
--- a/src/Shared/HandyControlDemo_Shared/Data/Model/DemoItemModel.cs
+++ b/src/Shared/HandyControlDemo_Shared/Data/Model/DemoItemModel.cs
@@ -4,6 +4,9 @@ namespace HandyControlDemo.Data
{
public class DemoItemModel : ObservableObject
{
+ private bool _isVisible = true;
+ private string _queriesText = string.Empty;
+
public string Name { get; set; }
public string GroupName { get; set; }
@@ -14,7 +17,15 @@ namespace HandyControlDemo.Data
public bool IsNew { get; set; }
- private bool _isVisible = true;
+ public string QueriesText
+ {
+ get => _queriesText;
+#if NET40
+ set => Set(nameof(QueriesText), ref _queriesText, value);
+#else
+ set => Set(ref _queriesText, value);
+#endif
+ }
public bool IsVisible
{
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml
index aecee549..f72204a1 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml
@@ -6,6 +6,7 @@
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
xmlns:ex="clr-namespace:HandyControlDemo.Tools.Extension"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+ xmlns:data="clr-namespace:HandyControlDemo.Data"
Margin="16,16,0,16"
CornerRadius="10"
Effect="{StaticResource EffectShadow4}">
@@ -51,7 +52,7 @@
-
+
diff --git a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs
index adec3756..224a8ae3 100644
--- a/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs
+++ b/src/Shared/HandyControlDemo_Shared/UserControl/Main/LeftMainContent.xaml.cs
@@ -68,6 +68,7 @@ namespace HandyControlDemo.UserControl
foreach (var item in ViewModelLocator.Instance.Main.DemoInfoCurrent.DemoItemList)
{
item.IsVisible = true;
+ item.QueriesText = string.Empty;
}
}
else
@@ -78,10 +79,12 @@ namespace HandyControlDemo.UserControl
if (item.Name.ToLower().Contains(key))
{
item.IsVisible = true;
+ item.QueriesText = _searchKey;
}
else if (item.TargetCtlName.Replace("DemoCtl", "").ToLower().Contains(key))
{
item.IsVisible = true;
+ item.QueriesText = _searchKey;
}
else
{
@@ -89,10 +92,12 @@ namespace HandyControlDemo.UserControl
if (!string.IsNullOrEmpty(name) && name.ToLower().Contains(key))
{
item.IsVisible = true;
+ item.QueriesText = _searchKey;
}
else
{
item.IsVisible = false;
+ item.QueriesText = string.Empty;
}
}
}
diff --git a/src/Shared/HandyControl_Shared/Controls/Text/HighlightTextBlock.cs b/src/Shared/HandyControl_Shared/Controls/Text/HighlightTextBlock.cs
new file mode 100644
index 00000000..102d7763
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Controls/Text/HighlightTextBlock.cs
@@ -0,0 +1,204 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Media;
+
+namespace HandyControl.Controls
+{
+ public class HighlightTextBlock : TextBlock
+ {
+ public static readonly DependencyProperty SourceTextProperty =
+ DependencyProperty.Register("SourceText", typeof(string), typeof(HighlightTextBlock),
+ new PropertyMetadata(null, OnSourceTextChanged));
+
+ public static readonly DependencyProperty QueriesTextProperty =
+ DependencyProperty.Register("QueriesText", typeof(string), typeof(HighlightTextBlock),
+ new PropertyMetadata(null, OnQueriesTextChanged));
+
+ public static readonly DependencyProperty HighlightBrushProperty =
+ DependencyProperty.Register("HighlightBrush", typeof(Brush), typeof(HighlightTextBlock),
+ new PropertyMetadata(Brushes.Yellow));
+
+ private static void OnSourceTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
+ ((HighlightTextBlock) d).RefreshInlines();
+
+ private static void OnQueriesTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
+ ((HighlightTextBlock) d).RefreshInlines();
+
+ ///
+ /// Replace the original property Text for binding text.
+ ///
+ ///
+ /// Don't use the property!
+ /// Because the has some unique behaviors
+ /// that is not needed at at ,
+ /// which will cause some unexpected issue.
+ ///
+ public string SourceText
+ {
+ get => (string) GetValue(SourceTextProperty);
+ set => SetValue(SourceTextProperty, value);
+ }
+
+ ///
+ /// Gets or sets the text that need to be highlighted.
+ /// It can be an array of text separated by spaces.
+ ///
+ public string QueriesText
+ {
+ get => (string) GetValue(QueriesTextProperty);
+ set => SetValue(QueriesTextProperty, value);
+ }
+
+ ///
+ /// Gets or sets the of the background of the highlight text.
+ ///
+ public Brush HighlightBrush
+ {
+ get => (Brush) GetValue(HighlightBrushProperty);
+ set => SetValue(HighlightBrushProperty, value);
+ }
+
+ private void RefreshInlines()
+ {
+ Inlines.Clear();
+
+ if (string.IsNullOrEmpty(SourceText)) return;
+ if (string.IsNullOrEmpty(QueriesText))
+ {
+ Inlines.Add(SourceText);
+ return;
+ }
+
+ var sourceText = SourceText;
+ var queries = QueriesText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
+ var intervals = from query in queries.Distinct()
+ from interval in GetQueryIntervals(sourceText, query)
+ select interval;
+ var mergedIntervals = MergeIntervals(intervals.ToList());
+ var fragments = SplitTextByOrderedDisjointIntervals(sourceText, mergedIntervals);
+
+ Inlines.AddRange(GenerateRunElement(fragments));
+ }
+
+ private IEnumerable GenerateRunElement(IEnumerable fragments)
+ {
+ return from item in fragments
+ select item.IsQuery
+ ? GetHighlightRun(item.Text)
+ : new Run(item.Text);
+ }
+
+ private Run GetHighlightRun(string highlightText)
+ {
+ var run = new Run(highlightText);
+ run.SetBinding(TextElement.BackgroundProperty, new Binding
+ {
+ Source = this,
+ Path = new PropertyPath(nameof(HighlightBrush)),
+ Mode = BindingMode.OneWay
+ });
+ return run;
+ }
+
+ private static IEnumerable SplitTextByOrderedDisjointIntervals(string sourceText, List mergedIntervals)
+ {
+ if (string.IsNullOrEmpty(sourceText)) yield break;
+
+ if (!mergedIntervals?.Any() ?? true)
+ {
+ yield return new Fragment { Text = sourceText, IsQuery = false };
+ yield break;
+ }
+
+ var range0 = mergedIntervals.First();
+ int start0 = range0.Start;
+ int end0 = range0.End;
+
+ if (start0 > 0) yield return new Fragment { Text = sourceText.Substring(0, start0), IsQuery = false };
+ yield return new Fragment { Text = sourceText.Substring(start0, end0 - start0), IsQuery = true };
+
+ int previousEnd = end0;
+ foreach (var range in mergedIntervals.Skip(1))
+ {
+ int start = range.Start;
+ int end = range.End;
+ yield return new Fragment { Text = sourceText.Substring(previousEnd, start - previousEnd), IsQuery = false };
+ yield return new Fragment { Text = sourceText.Substring(start, end - start), IsQuery = true };
+ previousEnd = end;
+ }
+
+ if (previousEnd < sourceText.Length)
+ yield return new Fragment { Text = sourceText.Substring(previousEnd), IsQuery = false };
+ }
+
+ private static List MergeIntervals(List intervals)
+ {
+ if (!intervals?.Any() ?? true) return new List();
+
+ intervals.Sort((x, y) => x.Start != y.Start ? x.Start - y.Start : x.End - y.End);
+
+ var pointer = intervals[0];
+ int startPointer = pointer.Start;
+ int endPointer = pointer.End;
+
+ var result = new List();
+ foreach (var range in intervals.Skip(1))
+ {
+ int start = range.Start;
+ int end = range.End;
+
+ if (start <= endPointer)
+ {
+ if (endPointer < end)
+ {
+ endPointer = end;
+ }
+ }
+ else
+ {
+ result.Add(new Range { Start = startPointer, End = endPointer });
+ startPointer = start;
+ endPointer = end;
+ }
+ }
+ result.Add(new Range { Start = startPointer, End = endPointer });
+ return result;
+ }
+
+ private static IEnumerable GetQueryIntervals(string sourceText, string query)
+ {
+ if (string.IsNullOrEmpty(sourceText) || string.IsNullOrEmpty(query)) yield break;
+
+ int nextStartIndex = 0;
+ while (nextStartIndex < sourceText.Length)
+ {
+ int index = sourceText.IndexOf(query, nextStartIndex, StringComparison.CurrentCultureIgnoreCase);
+
+ if (index == -1) yield break;
+
+ nextStartIndex = index + query.Length;
+ yield return new Range { Start = index, End = nextStartIndex };
+ }
+ }
+
+ private struct Fragment
+ {
+ public string Text { get; set; }
+
+ public bool IsQuery { get; set; }
+ }
+
+ private struct Range
+ {
+ public int Start { get; set; }
+
+ public int End { get; set; }
+ }
+ }
+}
diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
index f3c8ac5d..2444e73d 100644
--- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
+++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
@@ -83,6 +83,7 @@
+