在应用开发过程中,经常遇到这样的需求:通过关键字查找数据,把带有关键字的数据显示出来,同时在结果中高亮显示关键字。在web开发中,只需在关键字上加一层标签,然后设置标签样式就可以轻松实现。

在WPF中显示文本内容通常采用TextBlock控件,也可以采用类似的方式,通过内联流内容元素Run达到同样的效果:

<TextBlock FontSize="20">
<Run Text="Hel" /><Run Foreground="Red" Text="lo " /><Run Text="Word" />
</TextBlock>

需要注意的是每个Run之间不要换行,如果换行的话,每个Run之间会有间隙,看起来像增加了空格。

通过这种方式实现查找结果中高亮关键字,需要把查找结果拆分成三部分,然后绑定到Run元素的Text属性,或者在后台代码中使用TextBlockInlines属性添加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中指定内容高亮。

技术要点与实现

通过TextEffectPositionStartPositionCount以及Foreground属性设置字符串中需要高亮内容的起始位置、长度以及高亮颜色。定义附加属性允许TextBlock设置需要高亮的内容位置以及颜色。

  • 首先定义类ColoredLettering(并不要求继承DependencyObject)。
  • ColoredLettering中注册自定义的附加属性,注册附加属性方式与注册依赖属性类似,不过附加属性是用DependencyProperty.RegisterAttached来注册。
  • 给附加属性注册属性值变化事件,事件处理逻辑中设置TextEffectPositionStartPositionCount以及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实现查找结果高亮显示的更多相关文章

  1. WPF中TextBlock文本换行与行间距

    原文:WPF中TextBlock文本换行与行间距 换行符: C#代码中:\r\n 或  \r 或 \n XAML中: 或 注:\r 回车 (carriage return 缩写),\n 新行 (new ...

  2. 用WPF实现查找结果高亮显示

    概述 我们经常会遇到这样的需求:到数据库里查找一些关键字,把带这些关键字的记录返回显示在客户端上.但如果仅仅是单纯地把文本显示出来,那很不直观,用户不能很轻易地看到他们想找的内容,所以通常我们还要做到 ...

  3. WPF控件 RichTextBox查找定位匹配字符

    private void Search_Click(object sender, RoutedEventArgs e)//查询定位文本 { List<TextRange> textRang ...

  4. WPF TextBox/TextBlock 文本超出显示时,文本靠右显示

    文本框显示 文本框正常显示: 文本框超出区域显示: 实现方案 判断文本框是否超出区域 请见<TextBlock IsTextTrimmed 判断文本是否超出> 设置文本布局显示 1. Fl ...

  5. WPF 中 TextBlock 文本换行与行间距

    换行符: C#代码中:\r\n 或  \r 或 \n XAML中: 或 注:\r 回车 (carriage return 缩写),\n 新行 (new line 缩写). 行间距: LineHeigh ...

  6. 【WPF】TextBlock文本文字分段显示不同颜色

    需求:一行文字中,不同字符显示不同颜色.如注册页面,为表示必填项,在文本最后加一个红色的型号* 目标效果: 方法一: 用< StackPanel >嵌套两个< TextBlock & ...

  7. 【C#/WPF】TextBlock/TextBox/Label编辑文字的问题

    标题有点描述不清,就当是为了方便自己用时易于搜索到. 总之需求是:显示用户信息(文字)时,允许用户编辑自己的信息. 效果图如下: 点击[编辑]按钮前: 点击[编辑]按钮后,允许编辑: 别吐槽为甚性别还 ...

  8. wpf 类似TextBlock外观的Button的样式

    <Style x:Key="noborderbtnStyle" TargetType="{x:Type Button}"> <Setter P ...

  9. WPF中textBlock 变色功能

    <Window.Resources> <Storyboard x:Key="OnLoaded" RepeatBehavior="Forever" ...

  10. 年度巨献-WPF项目开发过程中WPF小知识点汇总(原创+摘抄)

    WPF中Style的使用 Styel在英文中解释为”样式“,在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,通过设置样式,使其WPF控件外观更加美化同时减少了 ...

随机推荐

  1. wmi搜集一台计算机的硬件信息

    作用: Python搜集一台计算机的硬件信息,借助模块:wmi,这个模块只支持window操作系统. 安装: pip install wmi 导入: import wmi 实例 c = wmi.WMI ...

  2. There is not enough memory to perform the requested operation

    今日在写bug 时 ide 突发脑溢血,崩溃了 一.修改用户目录下的 .vmoptions 找到C:\用户\用户名.WebStorm2018.1\config\webstorm64.exe.vmopt ...

  3. .NET周报 【5月第3期 2023-05-21】

    国内文章 C# 实现 Linux 视频会议(源码,支持信创环境,银河麒麟,统信UOS) https://www.cnblogs.com/shawshank/p/17390248.html 信创是现阶段 ...

  4. Pandas 加载数据的方法和技巧

    哈喽大家好,我是咸鱼 相信小伙伴们在学习 python 数据分析的过程中或多或少都会听说或者使用过 pandas pandas 是 python 的一个拓展库,常用于数据分析 今天咸鱼将介绍几个关于 ...

  5. Go语言如何判断两个对象是否相等

    1. 引言 在编程中,判断两个对象是否相等是一项常见的任务,同时判断对象是否相等在很多情况下都非常重要,例如: 单元测试:编写单元测试时,经常需要验证函数的输出是否符合预期,这涉及到比较对象是否相等. ...

  6. 使用 conda 和 Jupyter 在 R 中实现数据科学分析

    前两篇文章我们介绍了 Jupyter Notebook 的一些基础用法,今天我们来介绍一下如何使用 conda 和 Jupyter 在 R 中开始一个数据科学项目. 在开始之前我们先要明确一个概念:K ...

  7. 用R来分析洛杉矶犯罪

    由于微信不允许外部链接,你需要点击文章尾部左下角的 "阅读原文",才能访问文中链接. 洛杉矶市(Los Angeles)或"爵士乐的诞生地(The Birthplace ...

  8. NSDI-2023 微软论文:解构有状态网络功能

    本文通过chatgpt代理站(支持gpt4):gptschools.cn翻译整理 微软Azure对每个虚拟机进行了为期三个月的网络监控,获得了新建.并发.PPS等指标情况,发现: 1) 网络功能负载不 ...

  9. linux 服务器上查看日志的几种方式

      1.tail命令:tail -f filename 可以动态的查看日志的输出内容.     查看文件的最后几行,默认是10行,但是可以通过-n 参数来指定要查看的行数.     例如tail -n ...

  10. Codeforces Round #877 (Div. 2) A-E

    A 代码 #include <bits/stdc++.h> using namespace std; using ll = long long; bool solve() { int n; ...