WPF使用AvalonEdit实现代码高亮显示、搜索、替换功能

举报
步步为营 发表于 2023/02/03 17:31:40 2023/02/03
【摘要】 很多工程软件拥有自己定义的脚本语言,作为程序员用惯了具有高亮显示和智能提示功能的编辑器,所以针对特定的脚本自己开发一个编辑器。主要采用WPF、C#语言以及AvalonEdit控件。

WPF使用AvalonEdit实现代码高亮显示、搜索、替换功能

很多工程软件拥有自己定义的脚本语言,作为程序员用惯了具有高亮显示和智能提示功能的编辑器,所以针对特定的脚本自己开发一个编辑器。主要采用WPF、C#语言以及AvalonEdit控件。

AvlonEdit控件

AvalonEdit是基于WPF的代码显示控件,可以支持代码高亮显示、智能提示、代码折叠等功能。

AvalonEdit项目官网

在WPF中使用AvalonEdit非常简单,直接Nuget安装,然后引入命名空间xmlns:avalonEdit="http://icsharpcode.net/sharpdevelop/avalonedit",最后直接使用即可<avalonEdit:TextEditor/>。因为本文后面要实现自定义替换,需要对源码进行修改及重新编译,所以最好直接下载源码。

实现自定义高亮显示

AvalonEdit已经内置了C#、C++、Java等常见语言的高亮显示,如果要为自定义的语言进行语法高亮需要写一个*.xshd文件,该文件的基本使用如下:

<?xml version="1.0"?>
<SyntaxDefinition name="Custom Highlighting" xmlns="http://icsharpcode.net/sharpdevelop/syntaxdefinition/2008">
  <!--设置颜色与文本风格,如粗体,背景色等-->
  <Color name="Comment" foreground="#C6B1B1" exampleText="* comment"/>
  <Color name="Card" fontWeight="bold" foreground="#960092" exampleText="=CSTR"/>
  <Color name="Field" fontWeight="bold" foreground="#3A76D7" exampleText="CA"/>
	<!-- 主要的规则集 -->
	<RuleSet>
    <!--以//开头或者包裹在/*..*/中的文本使用Comment颜色-->
		<Span color="Comment" begin="//" />
		<Span color="Comment" multiline="true" begin="/\*" end="\*/" />
		
		<Span color="String">
			<Begin>"</Begin>
			<End>"</End>
      <!--可以定义规则子集-->
			<RuleSet>
				<Span begin="\\" end="." />
			</RuleSet>
		</Span>
		<!--定义关键词-->
		<Keywords fontWeight="bold" foreground="Blue">
			<Word>if</Word>
			<Word>else</Word>
			<!-- ... -->
		</Keywords>	
		<!-- 可以使用正则进行定义 -->
		<Rule foreground="DarkBlue">
            \b0[xX][0-9a-fA-F]+  # hex number
        |    \b
            (    \d+(\.[0-9]+)?   #number with optional floating point
            |    \.[0-9]+         #or just starting with floating point
            )
            ([eE][+-]?[0-9]+)? # optional exponent
        </Rule>
	</RuleSet>
</SyntaxDefinition>

自定义完*.xshd文件后,一定要设置文件的属性

image-20230203163254312

设置完成后,需要在程序中设置加载

 //注册自定义高亮
 IHighlightingDefinition customHighlighting;
 using (Stream s = typeof(MainWindow).Assembly.GetManifestResourceStream("NotConvertPeps.PEPSHighlighting.xshd"))
 {
     using (XmlReader reader = new XmlTextReader(s))
     {
         customHighlighting = HighlightingLoader.Load(reader, HighlightingManager.Instance);
     }
 }
//要设置一个后缀名字,在这里我设置了fre
 HighlightingManager.Instance.RegisterHighlighting("Custom Highlighting", new string[] { ".fre" }, customHighlighting);

 InitializeComponent();
//在InitializeComponent()之后使用,为txtEdit设置高亮语法
 txtEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinitionByExtension(".fre");

设置完成后,看下效果

image-20230203163618289

实现文本搜索

AvalonEdit已经具有了搜索功能,新版本只需使用ICSharpCode.AvalonEdit.Search.SearchPanel.Install(txtEditor);便可以使用Ctrl+F调出搜索栏,该搜索栏具有是否忽略大小写、是否全字匹配、是否使用正则三个设置项,而且还有背景显示、下拉框自动下拉等功能,基本满足要求。

1

实现文本替换

很遗憾AvalonEdit没有提供像搜索栏一样的功能,必须自己来实现。

自定义搜索栏用户控件

仿照VS的替换栏进行页面设计

image-20230203164735784

其中注册replaceContent、findContent、CareCase、MatchAll等依赖属性进行绑定,值得注意的是,用户控件在绑定的时候,source要用RelativeSource,否则不能实现数据更新,正确绑定方式如下:

<CheckBox
    x:Name="chxCareCase"
    IsChecked="{Binding CareCase,RelativeSource={RelativeSource AncestorType=local:ReplaceControl}}"
    Template="{StaticResource ToggleButtonControlTemplate2}"
    ToolTip="区分大小写" />

实现自定义搜索

因为替换操作仍然需要先查询再替换,所以需要自定义实现搜索。实现思路可以是得到textEditor中的text,然后使用string.index等方法进行,但是这样太麻烦,而且还需要自定义搜索结果的背景高亮以及文本选择。所以要换个思路,因为AvalonEdit已经提供了搜索功能,所以一定会有相关的接口,与查询有关的代码都存在于ICSharpCode.AvalonEdit.Search命名空间下,主要的查询方法存在于ICSharpCode.AvalonEdit.Search.SearchPanel中,其中最关键的查询方法是SearchStrategyFactory.Create(SearchPattern, !MatchCase, WholeWords, UseRegex ? SearchMode.RegEx : SearchMode.Normal);

除了该方法外,我们还需要进行上一个和下一个搜索以及搜索结果的背景高亮显示,这都和SearchResultBackgroundRenderer类相关,但是AvalonEdit官方源码中,该类的访问权限是Private,所以需要将访问权限改为public,然后重新编译。

实现自定义搜索功能C#代码

SearchResultBackgroundRenderer renderer = new SearchResultBackgroundRenderer();
void DoFind()
{
    renderer.CurrentResults.Clear();//清空已经搜索出的结果
    if (!string.IsNullOrEmpty(replaceUserControl.findContent))
    {
        //文字的背景高亮
        textArea.TextView.BackgroundRenderers.Clear();
        textArea.TextView.BackgroundRenderers.Add(renderer);
        //搜索的管件方法
        ISearchStrategy strategy = SearchStrategyFactory.Create(replaceUserControl.findContent, !replaceUserControl.CareCase, replaceUserControl.MatchAll, replaceUserControl.regex ? SearchMode.RegEx : SearchMode.Normal);
        var results = strategy.FindAll(textArea.Document, 0, textArea.Document.TextLength);
        //将搜索的结果全部加到renderer.CurrentResults中,方便后续的进行上一个、下一个搜索的展示
        foreach (SearchResult result in results)
        {
            renderer.CurrentResults.Add(result);
        }
    }
}

实现下一个

private void FindNext(object sender, RoutedEventArgs e)
{
    DoFind();
    
    SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset + 1);
    if (result == null)
        result = renderer.CurrentResults.FirstSegment;
    if (result != null)
    {
        SelectResult(result);
    }
}

实现上一个

private void FindPre(object sender, RoutedEventArgs e)
{
    DoFind();
    SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset);
    if (result != null)
        result = renderer.CurrentResults.GetPreviousSegment(result);
    if (result == null)
        result = renderer.CurrentResults.LastSegment;
    if (result != null)
    {
        SelectResult(result);
    }
}

实现效果

2

实现自定义替换

AvalonEdit提供了Document.Replace方法,可以直接使用

private void ReplaceNext(object sender, RoutedEventArgs e)
{
    string replace = replaceUserControl.replaceContent;
    DoFind();
    SearchResult result = renderer.CurrentResults.FindFirstSegmentWithStartAfter(textArea.Caret.Offset + 1);
    if (result == null)
        result = renderer.CurrentResults.FirstSegment;
    if (result != null)
    {
        SelectResult(result);
        this.txtEditor.Document.Replace(result.StartOffset, result.Length, replace);
        DoFind();//必须调用一次,不然查询出的字段背景色会乱
    }
}

img

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。