在 TextBlock 中自动检测 URL、电话号码、电子邮件 [英] Auto detect URL, phone number, email in TextBlock

查看:42
本文介绍了在 TextBlock 中自动检测 URL、电话号码、电子邮件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 UWP 中自动突出显示 URL、电子邮件和电话号码.这在 Android 中是可能的,但微软似乎已经忘记了这个功能.在我的用例中,我从网络服务中获取文本,因此我不知道网络平台上用户文本输入的文本格式.

I would like to automatically highlight URL, Email and phone number in UWP. It is possible in Android but it seems this features has been forgotten by Microsoft. In my use case, I get the text from a web service, so I don't know the text format which is a user text input on the web platform.

推荐答案

平台不支持此功能(目前).当我必须做同样的事情时,我已经结束了我自己的解决方案:

The platform is not supporting this feature (yet). When I've to do the same thing, I've ended with my own solution which is to:

  • 创建一个附加属性来接收要格式化的文本
  • 使用一些正则表达式从中提取 URL、电话号码、电子邮件地址
  • 生成一个内联集合,我将其注入到附加的 TextBlock 控件中

所使用的正则表达式涵盖了很多情况,但仍可能缺少一些边缘情况.

The regex used are covering a lot of cases but some edge cases can be still missing.

它是这样使用的:

<TextBlock  uwpext:TextBlock.InteractiveText="Here is a link www.bing.com to send to a@a.com or 0000000000" />

附加属性代码:

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// The regex to detect the URL from the text content
    /// It comes from https://gist.github.com/gruber/249502 (http://daringfireball.net/2010/07/improved_regex_for_matching_urls)
    /// </summary>
    private static readonly Regex UrlRegex = new Regex(@"(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'"".,<>?«»""‘’]))", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(500));

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// The regex to detect the email addresses
    /// It comes from https://msdn.microsoft.com/en-us/library/01escwtf.aspx
    /// </summary>
    private static readonly Regex EmailRegex    = new Regex(@"(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(500));

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// The regex to detect the phone numbers from the raw message
    /// </summary>
    private static readonly Regex PhoneRegex    = new Regex(@"\+?[\d\-\(\)\. ]{5,}", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// The default prefix to use to convert a relative URI to an absolute URI
    /// The Windows RunTime is only working with absolute URI
    /// </summary>
    private const string    RelativeUriDefaultPrefix    = "http://";


    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// The dependency property to generate an interactive text in a text block.
    /// When setting this property, we will parse the value and transform the hyperlink or the email address to interactive fields that the user can interact width.
    /// The raw text will be parsed and convert to a collection of inlines.
    /// </summary>
    public static readonly DependencyProperty InteractiveTextProperty = DependencyProperty.RegisterAttached("InteractiveText", typeof(string), typeof(TextBlock), new PropertyMetadata(null, OnInteractiveTextChanged));

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// The event callback for the interactive text changed event
    /// We will parse the raw text and generate the inlines that will wrap the interactive items (URL...)
    /// </summary>
    /// <param name="d">the object which has raised the event</param>
    /// <param name="e">the change information</param>
    private static void OnInteractiveTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBlock   = d as Windows.UI.Xaml.Controls.TextBlock;
        if(textBlock == null) return;

        // we remove all the inlines
        textBlock.Inlines.Clear();

        // if we have no data, we do not need to go further
        var rawText = e.NewValue as string;
        if(string.IsNullOrEmpty(rawText)) return;


        var lastPosition    = 0;
        var matches         = new Match[3];
        do
        {
            matches[0]  = UrlRegex.Match(rawText, lastPosition);
            matches[1]  = EmailRegex.Match(rawText, lastPosition);
            matches[2]  = PhoneRegex.Match(rawText, lastPosition);

            var firstMatch  = matches.Where(x => x.Success).OrderBy(x => x.Index).FirstOrDefault();
            if(firstMatch == matches[0])
            {
                // the first match is an URL
                CreateRunElement(textBlock, rawText, lastPosition, firstMatch.Index);
                lastPosition    = CreateUrlElement(textBlock, firstMatch);
            }
            else if(firstMatch == matches[1])
            {
                // the first match is an email
                CreateRunElement(textBlock, rawText, lastPosition, firstMatch.Index);
                lastPosition    = CreateContactElement(textBlock, firstMatch, null);
            }
            else if(firstMatch == matches[2])
            {
                // the first match is a phonenumber
                CreateRunElement(textBlock, rawText, lastPosition, firstMatch.Index);
                lastPosition    = CreateContactElement(textBlock, null, firstMatch);
            }
            else
            {
                // no match, we add the whole text
                textBlock.Inlines.Add(new Run { Text = rawText.Substring(lastPosition) });
                lastPosition    = rawText.Length;
            }  
        }
        while(lastPosition < rawText.Length);
    }

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// This method will extract a fragment of the raw text string, create a Run element with the fragment and
    /// add it to the textblock inlines collection
    /// </summary>
    /// <param name="textBlock">the textblock where to add the run element</param>
    /// <param name="rawText">the raw text where the fragment will be extracted</param>
    /// <param name="startPosition">the start position to extract the fragment</param>
    /// <param name="endPosition">the end position to extract the fragment</param>
    private static void CreateRunElement(Windows.UI.Xaml.Controls.TextBlock textBlock, string rawText, int startPosition, int endPosition)
    {
        var fragment    = rawText.Substring(startPosition, endPosition - startPosition);
        textBlock.Inlines.Add(new Run { Text = fragment });
    }

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// Create an URL element with the provided match result from the URL regex
    /// It will create the Hyperlink element that will contain the URL and add it to the provided textblock
    /// </summary>
    /// <param name="textBlock">the textblock where to add the hyperlink</param>
    /// <param name="urlMatch">the match for the URL to use to create the hyperlink element</param>
    /// <returns>the newest position on the source string for the parsing</returns>
    private static int CreateUrlElement(Windows.UI.Xaml.Controls.TextBlock textBlock, Match urlMatch)
    {
        Uri targetUri;
        if(Uri.TryCreate(urlMatch.Value, UriKind.RelativeOrAbsolute, out targetUri))
        {
            var link            = new Hyperlink();
            link.Inlines.Add(new Run { Text= urlMatch.Value });

            if(targetUri.IsAbsoluteUri)
                link.NavigateUri    = targetUri;
            else
                link.NavigateUri    = new Uri(RelativeUriDefaultPrefix + targetUri.OriginalString);


            textBlock.Inlines.Add(link);
        }
        else
        {
            textBlock.Inlines.Add(new Run { Text= urlMatch.Value });
        }

        return urlMatch.Index + urlMatch.Length;
    }

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// Create a hyperlink element with the provided match result from the regex that will open the contact application
    /// with the provided contact information (it should be a phone number or an email address
    /// This is used only if the email address / phone number is not prefixed with the mailto: / tel: scheme
    /// It will create the Hyperlink element that will contain the email/phone number hyperlink and add it to the provided textblock.
    /// Clicking on the link will open the contact application
    /// </summary>
    /// <param name="textBlock">the textblock where to add the hyperlink</param>
    /// <param name="emailMatch">the match for the email to use to create the hyperlink element. Set to null if not available but at least one of emailMatch and phoneMatch must be not null.</param>
    /// <param name="phoneMatch">the match for the phone number to create the hyperlink element. Set to null if not available but at least one of emailMatch and phoneMatch must be not null.</param>
    /// <returns>the newest position on the source string for the parsing</returns>
    private static int CreateContactElement(Windows.UI.Xaml.Controls.TextBlock textBlock, Match emailMatch, Match phoneMatch)
    {
        var currentMatch    = emailMatch ?? phoneMatch;

        var link            = new Hyperlink();
        link.Inlines.Add(new Run { Text= currentMatch.Value });
        link.Click          += (s, a) =>
        {
            var contact     = new Contact();
            if(emailMatch != null)  contact.Emails.Add(new ContactEmail { Address   = emailMatch.Value  });
            if(phoneMatch != null)  contact.Phones.Add(new ContactPhone { Number    = phoneMatch.Value.StripNonDigitsCharacters() });

            ContactManager.ShowFullContactCard(contact, new FullContactCardOptions());
        };

        textBlock.Inlines.Add(link);
        return currentMatch.Index + currentMatch.Length;
    }

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// Return the InteractiveText value on the provided object
    /// </summary>
    /// <param name="obj">the object to query</param>
    /// <returns>the InteractiveText value</returns>
    public static string GetInteractiveText(DependencyObject obj)
    {
        return (string) obj.GetValue(InteractiveTextProperty);
    }

    // -------------------------------------------------------------------------------------------
    /// <summary>
    /// SEt the InteractiveText value on the provided object
    /// </summary>
    /// <param name="obj">the object to query</param>
    /// <param name="value">the value to set</param>
    public static void SetInteractiveText(DependencyObject obj, string value)
    {
        obj.SetValue(InteractiveTextProperty, value);
    }

这篇关于在 TextBlock 中自动检测 URL、电话号码、电子邮件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆