WPF利用TextBlock实现查找结果高亮显示效果
作者:czwy
前言
在应用开发过程中,经常遇到这样的需求:通过关键字查找数据,把带有关键字的数据显示出来,同时在结果中高亮显示关键字。在web开发中,只需在关键字上加一层标签,然后设置标签样式就可以轻松实现。
在WPF中显示文本内容通常采用TextBlock
控件,也可以采用类似的方式,通过内联流内容元素Run
达到同样的效果:
<TextBlock FontSize="20"> <Run Text="Hel" /><Run Foreground="Red" Text="lo " /><Run Text="Word" /> </TextBlock>
需要注意的是每个Run
之间不要换行,如果换行的话,每个Run
之间会有间隙,看起来像增加了空格。
通过这种方式实现查找结果中高亮关键字,需要把查找结果拆分成三部分,然后绑定到Run
元素的Text
属性,或者在后台代码中使用TextBlock
的Inlines
属性添加Run
元素
textBlock1.Inlines.Add(new Run("hel")); textBlock1.Inlines.Add(new Run("lo ") { Foreground=new SolidColorBrush(Colors.Red)}); textBlock1.Inlines.Add(new Run("world"));
这种方法虽然可以达到效果,但显然与MVVM的思想不符。接下来本文介绍一种通过附加属性实现TextBlock
中指定内容高亮。
技术要点与实现
通过TextEffect
的PositionStart
、PositionCount
以及Foreground
属性设置字符串中需要高亮内容的起始位置、长度以及高亮颜色。定义附加属性允许TextBlock
设置需要高亮的内容位置以及颜色。
- 首先定义类
ColoredLettering
(并不要求继承DependencyObject
)。 - 在
ColoredLettering
中注册自定义的附加属性,注册附加属性方式与注册依赖属性类似,不过附加属性是用DependencyProperty.RegisterAttached
来注册。 - 给附加属性注册属性值变化事件,事件处理逻辑中设置
TextEffect
的PositionStart
、PositionCount
以及Foreground
实现内容高亮。
public class ColoredLettering { public static void SetColorStart(TextBlock textElement, int value) { textElement.SetValue(ColorStartProperty, value); } public static int GetColorStart(TextBlock textElement) { return (int)textElement.GetValue(ColorStartProperty); } // Using a DependencyProperty as the backing store for ColorStart. This enables animation, styling, binding, etc... public static readonly DependencyProperty ColorStartProperty = DependencyProperty.RegisterAttached("ColorStart", typeof(int), typeof(ColoredLettering), new FrameworkPropertyMetadata(0, OnColorStartChanged)); private static void OnColorStartChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBlock textBlock = d as TextBlock; if (textBlock != null) { if (e.NewValue == e.OldValue) return; if (e.NewValue is int) { int count = GetColorLength(textBlock); Brush brush = GetForeColor(textBlock); if ((int)e.NewValue <= 0 || count <= 0 || brush == TextBlock.ForegroundProperty.DefaultMetadata.DefaultValue) return; if (textBlock.TextEffects.Count != 0) { textBlock.TextEffects.Clear(); } TextEffect textEffect = new TextEffect() { Foreground = brush, PositionStart = (int)e.NewValue, PositionCount = count }; textBlock.TextEffects.Add(textEffect); } } } public static void SetColorLength(TextBlock textElement, int value) { textElement.SetValue(ColorLengthProperty, value); } public static int GetColorLength(TextBlock textElement) { return (int)textElement.GetValue(ColorLengthProperty); } // Using a DependencyProperty as the backing store for ColorStart. This enables animation, styling, binding, etc... public static readonly DependencyProperty ColorLengthProperty = DependencyProperty.RegisterAttached("ColorLength", typeof(int), typeof(ColoredLettering), new FrameworkPropertyMetadata(0, OnColorLengthChanged)); private static void OnColorLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBlock textBlock = d as TextBlock; if (textBlock != null) { if (e.NewValue == e.OldValue) return; if (e.NewValue is int) { int start = GetColorStart(textBlock); Brush brush = GetForeColor(textBlock); if ((int)e.NewValue <= 0 || start <= 0 || brush == TextBlock.ForegroundProperty.DefaultMetadata.DefaultValue) return; if (textBlock.TextEffects.Count != 0) { textBlock.TextEffects.Clear(); } TextEffect textEffect = new TextEffect() { Foreground = brush, PositionStart = start, PositionCount = (int)e.NewValue }; textBlock.TextEffects.Add(textEffect); } } } public static void SetForeColor(TextBlock textElement, Brush value) { textElement.SetValue(ColorStartProperty, value); } public static Brush GetForeColor(TextBlock textElement) { return (Brush)textElement.GetValue(ForeColorProperty); } // Using a DependencyProperty as the backing store for ForeColor. This enables animation, styling, binding, etc... public static readonly DependencyProperty ForeColorProperty = DependencyProperty.RegisterAttached("ForeColor", typeof(Brush), typeof(ColoredLettering), new PropertyMetadata(TextBlock.ForegroundProperty.DefaultMetadata.DefaultValue, OnForeColorChanged)); private static void OnForeColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { TextBlock textBlock = d as TextBlock; if (textBlock != null) { if (e.NewValue == e.OldValue) return; if (e.NewValue is Brush) { int start = GetColorStart(textBlock); int count = GetColorLength(textBlock); if (start <= 0 || count <= 0) return; if (textBlock.TextEffects.Count != 0) { textBlock.TextEffects.Clear(); } TextEffect textEffect = new TextEffect() { Foreground = (Brush)e.NewValue, PositionStart = start, PositionCount = count }; textBlock.TextEffects.Add(textEffect); } } } }
调用时只需在TextBlock
指定需要高亮内容的开始位置,内容长度以及高亮颜色即可。
<TextBlock local:ColoredLettering.ColorLength="{Binding Count}" local:ColoredLettering.ColorStart="{Binding Start}" local:ColoredLettering.ForeColor="{Binding ForeColor}" FontSize="20" Text="Hello World" />
总结
本文介绍的方法只是高亮第一个匹配到的关键字,如果需要高亮匹配到的所有内容,只需要对附加属性进行改造,以支持传入一组位置和颜色信息。
最后分享一个可以解析一组有限的HTML标记并显示它们的WPF控件HtmlTextBlock ,通过这个控件也可以实现查找结果中高亮关键字,甚至支持指定内容触发事件做一些逻辑操作。
到此这篇关于WPF利用TextBlock实现查找结果高亮显示效果的文章就介绍到这了,更多相关WPF TextBlock内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!