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));

Ответ 1

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

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

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

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

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