AvalonEdit: выделение текущей строки, даже если она не сфокусирована - PullRequest
13 голосов
/ 22 февраля 2011

Я использую 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 Ответ

13 голосов
/ 23 февраля 2011

Вот как я справился с этой работой.

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

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

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

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

...