AvalonEdit: выделить текущую строку, даже если она не сфокусирована

Я использую AvalonEdit и хочу, чтобы пользователь всегда мог видеть, на какой строке находится курсор, даже если редактор не имеет фокуса. С этой целью я нашел и адаптировал некоторый код, который использует BackgroundRenderer для выделения фона текущей строки.

К сожалению, если я изменю CaretOffset, когда редактор не сфокусирован, мой фоновый прямоугольник останется на линии, которая была текущей, когда редактор потерял фокус. Он не синхронизируется с новой текущей строкой, пока редактор снова не получит фокус.

Я выяснил, почему это происходит (только не как это исправить). Согласно комментариям к документу для IBackgroundRenderer, «фоновый рендерер будет рисовать только в том случае, если связанный с ним известный слой решит их отрисовать. Например, фоновые рендереры в слое курсора будут невидимы, когда курсор скрыт». Мой фоновый рендеринг живет на KnownLayer.Caret, так что да, я понимаю, почему он не обновляется, когда редактор не сфокусирован — это потому, что курсор тоже скрыт. (Учитывая это, я действительно удивлен, что мой прямоугольник остается видимым.)

Я попытался явно вызвать textEditor.TextArea.TextView.InvalidateLayer(KnownLayer.Caret) сразу после того, как я установил CaretOffset, но это не дало никакого эффекта - я предполагаю, что вызов был проигнорирован, потому что каретка была скрыта.

Каков наилучший способ принудительно обновить выделение текущей строки, даже если редактор не имеет фокуса?


Вот код моего класса. Я, конечно, готов отказаться от этого и использовать другой подход, если есть лучший способ сделать это.

public class HighlightCurrentLineBackgroundRenderer : IBackgroundRenderer
{
    private TextEditor _editor;

    public HighlightCurrentLineBackgroundRenderer(TextEditor editor)
    {
        _editor = editor;
    }

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

    public void Draw(TextView textView, DrawingContext drawingContext)
    {
        if (_editor.Document == null)
            return;

        textView.EnsureVisualLines();
        var currentLine = _editor.Document.GetLineByOffset(_editor.CaretOffset);
        foreach (var rect in BackgroundGeometryBuilder.GetRectsForSegment(textView, currentLine))
        {
            drawingContext.DrawRectangle(
                new SolidColorBrush(Color.FromArgb(0x40, 0, 0, 0xFF)), null,
                new Rect(rect.Location, new Size(textView.ActualWidth - 32, rect.Height)));
        }
    }
}

Затем в конструкторе моего UserControl я добавляю рендерер в редактор:

textEditor.TextArea.TextView.BackgroundRenderers.Add(
    new HighlightCurrentLineBackgroundRenderer(textEditor));

person Joe White    schedule 22.02.2011    source источник
comment
Только что использовал это, люблю эффект. Кроме того, я немного изменил его, чтобы учесть горизонтальную прокрутку, последней строкой будет: new Rect(new Point(rect.Location.X + textView.ScrollOffset.X, rect.Location.Y), new Size(textView .ActualWidth, rect.Height)));   -  person anakic    schedule 09.12.2015
comment
Неэффективно создавать SolidColorBrush в каждом цикле рисования. Создайте его один раз в конструкторе, заморозьте и укажите ссылку в методе Draw.   -  person Alexander Smirnov    schedule 26.05.2018


Ответы (1)


Вот как я закончил эту работу.

Во-первых, я изменил свойство Layer HighlightCurrentLineBackgroundRenderer, чтобы оно возвращало KnownLayer.Background. Фоновый слой виден, даже когда редактор не имеет фокуса, так что это решает исходную проблему.

Однако возникает новая проблема: фоновый слой перерисовывается только при определенных условиях, а «перемещение курсора» не является одним из этих условий — так что теперь выделение вообще не перемещается! (Ну, да, например, при прокрутке или выборе текста. Но это нежелательное поведение.) Но это легко решить; Мне просто нужно вручную аннулировать фоновый слой всякий раз, когда курсор перемещается:

textEditor.TextArea.Caret.PositionChanged += (sender, e) =>
    textEditor.TextArea.TextView.InvalidateLayer(KnownLayer.Background);

Это все, что нужно сделать — теперь выделение обновляется, даже если редактор не сфокусирован.

person Joe White    schedule 23.02.2011