【问题标题】:AvalonEdit show selection over other formattingAvalonEdit 在其他格式上显示选择
【发布时间】:2017-02-06 22:20:10
【问题描述】:

我目前使用以下代码作为 LineTransformer 和 AvalonEdit TextEditor。我希望能够通过选择突出显示当前的单个搜索结果,但是选择几乎不可见,因为DocumentColorizingTransformer 的格式优先于显示突出显示的文本。如何让突出显示的选择在格式化之前或之前显示?

public class ColorizeSearchResults : DocumentColorizingTransformer {

    public ColorizeSearchResults() : base() {
        SearchTerm = "";
        MatchCase = false;
    }

    public string SearchTerm { get; set; }
    public bool MatchCase { get; set; }

    protected override void ColorizeLine(DocumentLine line) {
        if (SearchTerm.Length == 0)
            return;

        int lineStartOffset = line.Offset;
        string text = CurrentContext.Document.GetText(line);
        int count = 0;
        int start = 0;
        int index;
        while ((index = text.IndexOf(SearchTerm, start, MatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase)) >= 0) {
            base.ChangeLinePart(
                lineStartOffset + index,
                lineStartOffset + index + SearchTerm.Length,
                (VisualLineElement element) => {
                    element.TextRunProperties.SetForegroundBrush(Brushes.White);
                    element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);
                });
            start = index + 1;
            count++;
        }
    }
}

Example of formatting showing over selection

【问题讨论】:

    标签: c# wpf avalonedit


    【解决方案1】:

    所以这是我的最终产品,几乎完全基于现有的 AvalonEdit SearchResultBackgroundRenderer。

    这与我帖子的着色器有点不同,因为您必须手动修改搜索结果,而不是由它为您完成。但这也可以节省一些计算时间。

    如果您的搜索不使用正则表达式,那么您可以轻松地修改 SearchResult 以改为只为构造函数传入起始偏移量和长度。

    /// <summary>A search result storing a match and text segment.</summary>
    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;
        }
    }
    
    /// <summary>Colorizes search results behind the selection.</summary>
    public class ColorizeSearchResultsBackgroundRenderer : IBackgroundRenderer {
    
        /// <summary>The search results to be modified.</summary>
        TextSegmentCollection<SearchResult> currentResults = new TextSegmentCollection<SearchResult>();
    
        /// <summary>Constructs the search result colorizer.</summary>
        public ColorizeSearchResultsBackgroundRenderer() {
            Background = new SolidColorBrush(Color.FromRgb(246, 185, 77));
            Background.Freeze();
        }
    
        /// <summary>Gets the layer on which this background renderer should draw.</summary>
        public KnownLayer Layer {
            get {
                // draw behind selection
                return KnownLayer.Selection;
            }
        }
    
        /// <summary>Causes the background renderer to draw.</summary>
        public void Draw(TextView textView, DrawingContext drawingContext) {
            if (textView == null)
                throw new ArgumentNullException("textView");
            if (drawingContext == null)
                throw new ArgumentNullException("drawingContext");
    
            if (currentResults == null || !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 (SearchResult result in currentResults.FindOverlappingSegments(viewStart, viewEnd - viewStart)) {
                BackgroundGeometryBuilder geoBuilder = new BackgroundGeometryBuilder();
                geoBuilder.AlignToWholePixels = true;
                geoBuilder.BorderThickness = 0;
                geoBuilder.CornerRadius = 0;
                geoBuilder.AddSegment(textView, result);
                Geometry geometry = geoBuilder.CreateGeometry();
                if (geometry != null) {
                    drawingContext.DrawGeometry(Background, null, geometry);
                }
            }
        }
    
        /// <summary>Gets the search results for modification.</summary>
        public TextSegmentCollection<SearchResult> CurrentResults {
            get { return currentResults; }
        }
    
        /// <summary>Gets or sets the background brush for the search results.</summary>
        public Brush Background { get; set; }
    }
    

    为了使用后台渲染器:

    var searchColorizor = new ColorizeSearchResultsBackgroundRenderer();
    textEditor.TextArea.TextView.BackgroundRenderers.Add(searchColorizor);
    

    【讨论】:

      【解决方案2】:

      请参阅 http://avalonedit.net/documentation/html/c06e9832-9ef0-4d65-ac2e-11f7ce9c7774.htm 了解 AvalonEdit 渲染流程。

      选择层在文本之前呈现。因此,如果文本有背景,它会覆盖选择背景。幸运的是,我们可以将背景设置为 Brush.Transparent(或 Selection.Brush 和您自己的颜色的混合)。

      解决方案:我修改了 SelectionColorizer 代码,将选择背景重置为透明:

      class SelectionColorizerWithBackground : ColorizingTransformer
      {
          ICSharpCode.AvalonEdit.Editing.TextArea _textArea;
      
          public SelectionColorizerWithBackground(
              ICSharpCode.AvalonEdit.Editing.TextArea textArea)
          {
              if (textArea == null)
                  throw new ArgumentNullException("textArea");
              this._textArea = textArea;
          }
      
          protected override void Colorize(ITextRunConstructionContext context)
          {
              int lineStartOffset = context.VisualLine.FirstDocumentLine.Offset;
      
              int lineEndOffset = context.VisualLine.LastDocumentLine.Offset +
                  context.VisualLine.LastDocumentLine.TotalLength;
      
              foreach (var segment in _textArea.Selection.Segments)
              {
                  int segmentStart = segment.StartOffset;
                  if (segmentStart >= lineEndOffset)
                      continue;
      
                  int segmentEnd = segment.EndOffset;
                  if (segmentEnd <= lineStartOffset)
                      continue;
      
                  int startColumn;
                  if (segmentStart < lineStartOffset)
                      startColumn = 0;
                  else
                      startColumn = context.VisualLine.ValidateVisualColumn(
                          segment.StartOffset, segment.StartVisualColumn,
                          _textArea.Selection.EnableVirtualSpace);
      
                  int endColumn;
                  if (segmentEnd > lineEndOffset)
                      endColumn =
                          _textArea.Selection.EnableVirtualSpace
                              ? int.MaxValue
                              : context.VisualLine
                                       .VisualLengthWithEndOfLineMarker;
                  else
                      endColumn = context.VisualLine.ValidateVisualColumn(
                          segment.EndOffset, segment.EndVisualColumn,
                          _textArea.Selection.EnableVirtualSpace);
      
                  ChangeVisualElements(
                      startColumn, endColumn,
                      element => {
                          element.TextRunProperties.SetBackgroundBrush(
                              System.Windows.Media.Brushes.Transparent);
                          if (_textArea.SelectionForeground != null)
                          {
                              element.TextRunProperties.SetForegroundBrush(
                                  _textArea.SelectionForeground);
                          }
                      });
              }
          }
      }
      

      要使用代码,您应该执行以下操作:

      var lineTransformers = textEditor.TextArea.TextView.LineTransformers;
      
      // Remove the original SelectionColorizer.
      // Note: if you have syntax highlighting you need to do something else
      // to avoid clearing other colorizers. If too complicated you can skip
      // this step but to suffer a 2x performance penalty.
      lineTransformers.Clear();
      
      lineTransformers.Add(new ColorizeSearchResults());
      lineTransformers.Add(
          new SelectionColorizerWithBackground(textEditor.TextArea));
      

      【讨论】:

        【解决方案3】:

        在我广泛尝试了我的解决方案之后,我想补充几点:

        • 虽然我上面的其他解决方案似乎有效,但当应该平铺矩形时,您会遇到一些亚像素伪影。如果这是不可接受的,您可以实现 IBackgroundRenderer。 (这恰好是我选择的解决方案。)如果你想要一些代码,你可以在这里请求,但我怀疑它是否有用。

        • 顺便说一句,因为您的问题是关于搜索结果的,您很可能可以使用未修改的https://github.com/icsharpcode/AvalonEdit/blob/697ff0d38c95c9e5a536fbc05ae2307ec9ef2a63/ICSharpCode.AvalonEdit/Search/SearchResultBackgroundRenderer.cs(如果您不想要圆形边框,也可以修改它)。

        • 您可以使用element.BackgroundBrush = Brushes.Magenta; 代替element.TextRunProperties.SetBackgroundBrush(Brushes.Magenta);。 AvalonEdit 似乎使用半径为 3 像素的矩形绘制背景。

        • 还有一个从 AvalonEdit 5.01 开始的 RichTextColorizer。我不知道如何使用它,因为它没有在其他文件中引用。并且可能存在上一段中的(最可能不需要的)圆角矩形。

        【讨论】:

        • 一年后我用不同的程序回到这个问题并尝试了IBackgroundRenderer。除了绘制的矩形略大于选择矩形但不是什么大问题之外,它工作得很好。
        • 显然从荧光笔上取下笔刷甚至解决了这个小问题。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-06-04
        • 1970-01-01
        • 2015-05-24
        • 2014-07-25
        • 2013-11-21
        • 1970-01-01
        • 2014-07-16
        相关资源
        最近更新 更多