Пользовательские гиперссылки с помощью AvalonEdit

Я пытаюсь создать пользовательские гиперссылки с помощью AvalonEdit. Я создал генератор (на основе образца), который распознает синтаксис, и я могу установить Uri:

  public class LinkGenerator : VisualLineElementGenerator
  {
    readonly static Regex imageRegex = new Regex(@"<mylink>", RegexOptions.IgnoreCase);

    public LinkGenerator()
    {}

    Match FindMatch(int startOffset)
    {
        // fetch the end offset of the VisualLine being generated
        int endOffset = CurrentContext.VisualLine.LastDocumentLine.EndOffset;
        TextDocument document = CurrentContext.Document;
        string relevantText = document.GetText(startOffset, endOffset - startOffset);
        return imageRegex.Match(relevantText);
    }

    /// Gets the first offset >= startOffset where the generator wants to construct
    /// an element.
    /// Return -1 to signal no interest.
    public override int GetFirstInterestedOffset(int startOffset)
    {
        Match m = FindMatch(startOffset);
        return m.Success ? (startOffset + m.Index) : -1;
    }

    /// Constructs an element at the specified offset.
    /// May return null if no element should be constructed.
    public override VisualLineElement ConstructElement(int offset)
    {
        Match m = FindMatch(offset);
        // check whether there's a match exactly at offset
        if (m.Success && m.Index == 0)
        {
            var line = new VisualLineLinkText(CurrentContext.VisualLine, m.Length);

            line.NavigateUri = new Uri("http://google.com");
            return line;
        }
        return null;
    }
}

Однако есть две проблемы, которые я не могу понять:

  1. Что мне передать конструктору VisualLineLinkText, чтобы упростить текст, чтобы сказать «MyLink»?

  2. Где мне поместить обработчик событий, который будет получать RequestNavigateEventArgs, чтобы я мог переопределить поведение щелчка?


person Chris    schedule 07.02.2015    source источник


Ответы (1)


Мне нужно было использовать объекты стиля гиперссылки в AvalonEdit, но только для использования стиля «Перейти к определению», а не для веб-гиперссылок. Я не хотел, чтобы веб-браузер запускался, и мне нужно было самому перехватывать событие клика по гиперссылке в коде.

С этой целью я создал новый класс, наследуемый от VisualLineText. Он содержит событие CustomLinkClicked, которое передает строку обработчику событий.

/// <summary>
/// VisualLineElement that represents a piece of text and is a clickable link.
/// </summary>
public class CustomLinkVisualLineText : VisualLineText
{

    public delegate void CustomLinkClickHandler(string link);

    public event CustomLinkClickHandler CustomLinkClicked;

    private string Link { get; set; }

    /// <summary>
    /// Gets/Sets whether the user needs to press Control to click the link.
    /// The default value is true.
    /// </summary>
    public bool RequireControlModifierForClick { get; set; }

    /// <summary>
    /// Creates a visual line text element with the specified length.
    /// It uses the <see cref="ITextRunConstructionContext.VisualLine"/> and its
    /// <see cref="VisualLineElement.RelativeTextOffset"/> to find the actual text string.
    /// </summary>
    public CustomLinkVisualLineText(string theLink, VisualLine parentVisualLine, int length)
        : base(parentVisualLine, length)
    {
        RequireControlModifierForClick = true;
        Link = theLink;
    }


    public override TextRun CreateTextRun(int startVisualColumn, ITextRunConstructionContext context)
    {
        TextRunProperties.SetForegroundBrush(Brushes.GreenYellow);
        TextRunProperties.SetTextDecorations(TextDecorations.Underline);
        return base.CreateTextRun(startVisualColumn, context);
    }

    bool LinkIsClickable()
    {
        if (string.IsNullOrEmpty(Link))
            return false;
        if (RequireControlModifierForClick)
            return (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;
        else
            return true;
    }


    protected override void OnQueryCursor(QueryCursorEventArgs e)
    {
        if (LinkIsClickable())
        {
            e.Handled = true;
            e.Cursor = Cursors.Hand;
        }
    }

    protected override void OnMouseDown(MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Left && !e.Handled && LinkIsClickable())
        {

            if (CustomLinkClicked != null)
            {
                CustomLinkClicked(Link);
                e.Handled = true;
            }

        }
    }

    protected override VisualLineText CreateInstance(int length)
    {

        var a = new CustomLinkVisualLineText(Link, ParentVisualLine, length)
        {                
            RequireControlModifierForClick = RequireControlModifierForClick
        };

        a.CustomLinkClicked += link => ApplicationViewModel.Instance.ActiveCodeViewDocument.HandleLinkClicked(Link);
        return a;
    }
}

Поскольку эти элементы динамически создаются и уничтожаются во время выполнения, мне пришлось зарегистрировать событие щелчка в статическом экземпляре класса для обработки.

a.CustomLinkClicked += link => ApplicationViewModel.Instance.ActiveCodeViewDocument.HandleLinkClicked(Link);

Когда вы нажимаете на ссылку (используя Ctrl-щелчок, если указано), событие будет запущено. Вы можете заменить строку «Link» любым другим классом, который вам нужен. Вам нужно будет заменить строку «ApplicationViewModel.Instance.ActiveCodeViewDocument.HandleLinkClicked(Link)» на то, к чему у вас есть доступ в вашем коде.

person Alex Hopkins    schedule 13.08.2015
comment
Извините, что копаюсь в этом старом ответе, но навигация в стиле Jump To Definition - это именно то, чего я пытаюсь достичь. Я просто не понимаю, где совпадение происходит при использовании VisualLineText. Могу ли я как-то подключить это к Highlighting Engine? - person themightylc; 30.10.2017
comment
Не обращайте внимания на комментарий выше. Подсветка синтаксиса происходит в CreateInstance Sub - person themightylc; 30.10.2017