I am relatively new to C# and I'm working on a simple application that makes use of AvalonEdit as a small code editor embedded in the bottom of the UI.
I want to highlight all occurrences of a selected word. Right now I have this working using a DocumentColorizingTransformer
as outlined by Suplanus in this question.
My code is basically identical to the implementation posted there, but it has its downsides. Mainly that the default highlighting is fighting with the new LineTransformers highlighting and there are visual artifacts that can't be avoided.
So I came across this question where Siegfried (partially) explains a way to accomplish this by using an IBackgroundRenderer
class. Unfortunately there isn't a lot of code to go on. Just an outline of how to structure the draw
function. There is another question where trigger_segfault posts some similar code utilizing an IBackgroundRenderer
but the goal is not to highlight matching words, but search results.
So I've kind of cobbled together what I can between all of these questions and arrived here:
(Removed. See bottom of post for working code)
My question is simply, how do I pass in the matching words? Do I assign an event handler to the "SelectionChanged" event: txtSvgSource.TextArea.SelectionChanged += ValidateSelection;
and then implement a setter in my HighlightSelectedWordBackgroundRenderer
class to update currentResults
?
I just don't understand how to pass in the words to highlight.
I'd like to move away from my current DocumentColorizingTransformer
approach and properly implement the IBackgroundRenderer
instead.
Anyone with experience using AvalonEdit that could point me in the right direction, your help would be extremely appreciated!
Thanks so much.
Edit 2:
I figured everything out. Here's the final working code if anyone finds this useful:
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Rendering;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace SVGRecolorTool {
public class SearchResult : TextSegment {
/// <summary>The regex match for the search result.</summary>
public Match Match { get; }
/// <summary>Constructs the search result from the match.</summary>
public SearchResult(Match match) {
this.StartOffset = match.Index;
this.Length = match.Length;
this.Match = match;
}
}
public class HighlightSelectedWordBackgroundRenderer : IBackgroundRenderer {
private TextEditor _editor;
TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
public HighlightSelectedWordBackgroundRenderer(TextEditor editor) {
_editor = editor;
Background = new SolidColorBrush(Color.FromArgb(90, 255, 255, 255));
Background.Freeze();
}
public KnownLayer Layer {
get { return KnownLayer.Caret; }
}
public void Draw(TextView textView, DrawingContext drawingContext) {
if (!textView.VisualLinesValid)
return;
var visualLines = textView.VisualLines;
if (visualLines.Count == 0)
return;
int viewStart = visualLines.First().FirstDocumentLine.Offset;
int viewEnd = visualLines.Last().LastDocumentLine.EndOffset;
foreach (TextSegment result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
geoBuilder.AlignToWholePixels = true;
geoBuilder.CornerRadius = 0;
geoBuilder.AddSegment(textView, result);
Geometry geometry = geoBuilder.CreateGeometry();
if (geometry != null) {
drawingContext.DrawGeometry(Background, null, geometry);
}
}
}
public TextSegmentCollection<SearchResult> CurrentResults {
get { return currentResults; }
}
public Brush Background { get; set; }
}
}
_renderer = new HighlightSelectedWordBackgroundRenderer(txtSvgSource);
txtSvgSource.TextArea.TextView.BackgroundRenderers.Add(_renderer);
txtSvgSource.TextArea.SelectionChanged += HighlightSelection;
private void HighlightSelection(object sender, EventArgs e) {
_renderer.CurrentResults.Clear();
if(txtSvgSource.SelectedText.Length > 2) {
string pattern = @"\b" + txtSvgSource.SelectedText + @"\b";
Regex rg = new Regex(pattern);
MatchCollection matchedSelections = rg.Matches(txtSvgSource.Text);
if (matchedSelections.Count > 1) {
foreach (Match result in matchedSelections) {
_renderer.CurrentResults.Add(new SearchResult(result));
}
} else {
_renderer.CurrentResults.Clear();
}
} else {
_renderer.CurrentResults.Clear();
}
txtSvgSource.TextArea.TextView.InvalidateLayer(KnownLayer.Selection);
}