I'm working on a project that has taken a dependency on AvalonEdit to manage a document. Certain lines in this document are flagged to use a specific indentation which cannot be modified from within AvalonEdit.
I've managed to achieve this with VisualLineElements
that are injected at the start of these flagged lines. The visual elements have a DocumentLength
of 0
and a VisualLength
the size of the indentation. I also overrode the GetNextCaretPosition
method to "push" the caret either to the left of the VisualLineElement
(in the case of backwards movement) or to the right (in the case of forward movement).
Here's a version of my VisualLineElement
with reduced formatting (note that the element is always injected at VisualColumn == 0
) :
public class MyLineFormatElement : VisualLineElement
{
private readonly string _elementText;
public MyLineFormatElement (string elementText)
: base(elementText.Length, 0)
{
_elementText = elementText;
}
public override bool CanSplit
{
get
{
return false;
}
}
public override int GetNextCaretPosition(int visualColumn, System.Windows.Documents.LogicalDirection direction, ICSharpCode.AvalonEdit.Document.CaretPositioningMode mode)
{
if (visualColumn >= VisualColumn && visualColumn <= VisualColumn + VisualLength)
{
return direction == LogicalDirection.Forward ? VisualColumn + VisualLength : VisualColumn;
}
return base.GetNextCaretPosition(visualColumn, direction, mode);
}
public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
{
return new TextCharacters(_elementText, TextRunProperties);
}
}
This works great except for one issue I'm having: I can still place the caret at the start of the line. From a user standpoint, this is very confusing. What's worse, pressing "delete" when at the start of the line, or pressing "backspace" when at the end of the indentation simply moves the caret without making any modification. As I understand it, the delete/backspace problem would be resolved if the caret couldn't be placed at the line start (based on the code in CaretNavigationCommandHandler.OnMoveCaret
).
My best guess as to how to fix this is to override the HandlesLineBorders
property so it returns true and then modify GetNextCaretPosition
so that:
1. When moving forward over the element, return VisualColumn + VisualLength
(this is already the case)
2. When moving backwards over the element, return the previous line's last visual column (I don't know how to do this)
Is the last part of #2 possible? If so, how do I do that? If not, are there any other approaches I should try? I'd really like to stick with VisualLineElements
to keep a clean separation between the actual content of the document and the way it's rendered.