Question

I'm creating a view for patch files using AvalonEdit, and I want to make it so that diffs are highlighted across the entire line, not just the text background - similar to what GitHub for Windows does today:

I'm new to AvalonEdit, so I'm not sure the best way to do this. Here's what I've found so far:

  • Override VisualLineElementGenerator in order to create an additional TextSpan that is the length of the control. This seems Tricky.

  • Create a new control to add to TextView.Layers in the background and OnRender in the green/red by hand - this seems more promising, but it's not super clear what event I need to hook in order to detect when to re-render.

  • Override TextView - this seems like overkill.

Edit: Here's what happens with a simple syntax highlighter, which is what I don't want:

Was it helpful?

Solution 2

Create a new control to add to TextView.Layers in the background and OnRender in the green/red by hand

You don't have to create a new layer to render in something in the background: you can add your implementation of IBackgroundRenderer to textView.BackgroundRenderers to render something in the background of an existing layer.

it's not super clear what event I need to hook in order to detect when to re-render

That would be: textView.VisualLinesChanged. But you don't need that if you use IBackgroundRenderer as the existing layers are already re-rendered by AvalonEdit.

OTHER TIPS

As Daniel mentioned, I discovered Background Renderers via the Wiki page and it worked great. Here's what I ended up doing - it can probably be made a bit more efficient but it's good enough for now:

public class DiffLineBackgroundRenderer : IBackgroundRenderer
{
    static Pen pen;

    static SolidColorBrush removedBackground;
    static SolidColorBrush addedBackground;
    static SolidColorBrush headerBackground;

    FileDiffView host;

    static DiffLineBackgroundRenderer()
    {
        removedBackground = new SolidColorBrush(Color.FromRgb(0xff, 0xdd, 0xdd)); removedBackground.Freeze();
        addedBackground = new SolidColorBrush(Color.FromRgb(0xdd, 0xff, 0xdd)); addedBackground.Freeze();
        headerBackground = new SolidColorBrush(Color.FromRgb(0xf8, 0xf8, 0xff)); headerBackground.Freeze();

        var blackBrush = new SolidColorBrush(Color.FromRgb(0, 0, 0)); blackBrush.Freeze();
        pen = new Pen(blackBrush, 0.0);
    }

    public DiffLineBackgroundRenderer(FileDiffView host)
    {
        this.host = host;
    }

    public KnownLayer Layer 
    {
        get { return KnownLayer.Background; }
    }

    public void Draw(TextView textView, DrawingContext drawingContext)
    {
        foreach (var v in textView.VisualLines) 
        {
            var rc = BackgroundGeometryBuilder.GetRectsFromVisualSegment(textView, v, 0, 1000).First();
            // NB: This lookup to fetch the doc line number isn't great, we could
            // probably do it once then just increment.
            var linenum = v.FirstDocumentLine.LineNumber - 1;
            if (linenum >= host.ViewModel.Lines.Count) continue;

            var diffLine = host.ViewModel.Lines[linenum];

            if (diffLine.Style == DiffLineStyle.Context) continue;

            var brush = default(Brush);
            switch (diffLine.Style)
            {
            case DiffLineStyle.Header:
                brush = headerBackground;
                break;
            case DiffLineStyle.Added:
                brush = addedBackground;
                break;
            case DiffLineStyle.Deleted:
                brush = removedBackground;
                break;
            }

            drawingContext.DrawRectangle(brush, pen, 
                new Rect(0, rc.Top, textView.ActualWidth, rc.Height));
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top