2016-07-05 5 views
1

Je voudrais mettre en évidence automatiquement URL, Email et numéro de téléphone dans UWP. C'est possible sous Android mais il semble que cette fonctionnalité ait été oubliée par Microsoft. Dans mon cas d'utilisation, je reçois le texte d'un service Web, donc je ne connais pas le format de texte qui est une entrée de texte utilisateur sur la plate-forme Web.Auto détecter URL, numéro de téléphone, email dans TextBlock

Répondre

5

La plateforme ne prend pas encore en charge cette fonctionnalité. Quand je l'ai à faire la même chose, je l'ai fini avec ma propre solution qui consiste à:

  • créer une propriété attachée réception du texte pour mettre en forme
  • utiliser une expression régulière pour extraire les URL, les numéros de téléphone , adresses e-mail de ce
  • génèrent une collection Inlines que je suis injection au contrôle TextBlock attaché

l'expression régulière sont couvrant un grand nombre de cas, mais certains cas de bord peuvent être toujours portés disparus.

Il est utilisé de cette façon:

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

Le code de la propriété ci-joint:

// ------------------------------------------------------------------------------------------- 
    /// <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); 
    } 
+0

Merci pour votre code, mais il semble que j'ai un problème avec elle. J'ai eu une erreur d'initialisation: "Le texte associé à ce code d'erreur est introuvable. J'ai échoué à assigner à la propriété 'MyProject.TextBlockExtensions.InteractiveText'. [Ligne: 38 Position: 20]" –

+0

Sans plus de détails, il est difficile pour voir ce qui ne va pas. Selon le message d'erreur, il peut y avoir un problème de type avec votre contrôle. La propriété peut uniquement être liée à un TextBox. Si vous essayez de le lier à autre chose, cela provoquera ce genre d'erreur. Voir cette question pour plus de détails: http://stackoverflow.com/questions/17971195/xamlparseexception-failed-to-assign-to-property-binding-not-working-with-attach. Vous trouverez également des informations utiles ici: http://stackoverflow.com/questions/5832208/wpf-attached-property-data-binding. J'espère que ça aide – Vincent

+0

Merci Vincent, j'ai trouvé mon erreur: mon cours d'extension n'était pas public. Maintenant, cela semble bien fonctionner. –