【问题标题】:Custom hyperlinks using AvalonEdit使用 AvalonEdit 的自定义超链接
【发布时间】:2023-03-08 11:54:01
【问题描述】:

我正在尝试使用 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 的事件处理程序,以便我可以覆盖点击行为?

【问题讨论】:

    标签: c# wpf avalonedit


    【解决方案1】:

    我需要在 AvalonEdit 中使用超链接样式对象,但仅限于“跳转到定义”样式使用,而不是用于 Web 超链接。我不想启动网络浏览器,我需要自己在代码中捕获超链接点击事件。

    为此,我创建了一个继承自 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-单击),它将触发该事件。您可以将“链接”字符串替换为您需要的任何其他类。您需要将“ApplicationViewModel.Instance.ActiveCodeViewDocument.HandleLinkClicked(Link)”行替换为您在代码中可以访问的内容。

    【讨论】:

    • 很抱歉深入研究这个旧答案,但“跳转到定义”风格的导航正是我想要实现的。我只是不知道在您使用 VisualLineText 时“匹配”发生在哪里。我可以以某种方式将其连接到突出显示引擎吗?
    • 别介意上面的评论。语法高亮发生在 CreateInstance Sub
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-20
    • 2015-06-03
    • 1970-01-01
    • 2020-10-01
    • 2018-08-15
    相关资源
    最近更新 更多