Pull to refresh
0
Rating

Автоматическое выделение ссылок в универсальных приложениях Windows

Edusty corporate blog Development of mobile applications *Development for Windows Phone *Development for Windows *
При разработке кроссплатформенного приложения встаёт вопрос об унификации функционала между разными платформами. Когда мы разрабатывали Edusty, мы столкнулись с неожиданной для нас проблемой — отсутствием встроенной функции автовыделения ссылок в тексте на платформах Windows/Windows Phone, которая присутствует на платформах Android и iOS. Более того — мы не нашли даже сторонних библиотек, реализующих этот функционал. Пришлось реализовывать этот функционал самим. О том, что получилось, будет рассказано в этой статье.




На странице, где необходимо отобразить текст со ссылками, расположен контрол RichTextBlock. Этот контрол не поддерживает MVVM-привязку данных, поэтому заполнять его пришлось «по старинке».

<RichTextBlock Margin="20" x:Name="RTB" FontSize="20"/>

Заполнить RichTextBlock можно тремя способами:
1. Статичная XAML разметка прямо в коде страницы.
2. Программное заполнение коллекции BlockCollection .Blocks. Обычно она заполняется объектами типа Paragraph, которые инициализируются объектами, наследующими класс Inline (например, Run, Hyperlink и так далее).
3. Так же, как и во втором случае, заполнение коллеции Blocks, однако формирование объекта Paragraph происходит с помощью статичного класса XamlReader из XAML-разметки (сформированной в строкой форме).

В данном случае самым оптимальным будет третий метод, поскольку позволяет максимально гибко формировать разметку. Для того, чтобы распарсить xaml-строку в объекты, необходимо вызывать метод XamlReader.Load(xamlString). Данный метод возвращает object, который можно привести (в нашем случае) к типу Paragraph и добавить к RichTextBlock.Blocks.

RTB.Blocks.Add((Paragraph)XamlReader.Load(xamlString));


Формирование XAML-строки



И так, у нас есть входная строка, содержащая некий текст со ссылками или без них, а на выходе необходимо получить строку с валидной xaml разметкой для RichTextBlock (тег Paragraph), где все ссылки были бы в тегах Hyperlink, а обычный текст в тегах Run.

Для этого весь текст делится в массив слов по пробелам, затем выходная строка начинает формироваться таким образом, чтобы все теги всегда были закрыты при любой входной строке.
1. В самое начало текста добавляется незакрытый тег Run.
2. Запускается цикл по словам, где каждое слово будет проверяться с помощью регулярного выражения, является ли оно ссылкой или нет. Если является, то происходит закрытие тэга Run и вставка тега Hyperlink с соответствующей ссылкой, после чего снова открывается тег Run. Если же текущее слово не является ссылкой, то просто записываем данное слово к результату и переходим к следующему слову.
3. Когда все слова перебраны, то необходимо закрыть тег Run.

С обработкой ссылок не всё так просто, как может показаться на первый взгляд. Для начала определим, какие бывают ссылки: с указанием протокола, без него, с доменом, с ip адресом, с портом или без, с параметрами или без них.
В исходном тексте ссылки могут быть как уже URL-кодированы, так и нет. В последнем случае они могут содержать символы, из-за которых xaml разметка становится не валидной, поэтому ссылку необходимо обработать с помощью метода Uri.EscapeUriString(), который URL-кодирует только параметры ссылки, но не протокол, домен или порт. Однако это ещё не всё. URL-кодирование не заменяет символ '&', однако этот символ также делает xaml разметку не валидной, поэтому его следует заменять на его html-код '&аmp;'.
Также особенностью Windows-платформ является то, что чтобы открыть ссылку в другом приложении, ОС смотрит, какое приложение установлено по умолчанию для протокола, указанного в этой ссылке (например, httр://), поэтому, если протокол не указан, открыть такую ссылку ОС не может (более того, это даже вызовет исключение UriFormatException). Так что к любой ссылке, где не указан протокол, необходимо добавить по умолчанию протокол httр://.

Исходный текст иногда может содержать различные символы, нарушающие xaml разметку, поэтому его необходимо HTML-кодировать перед помещением в тег Run с помощью метода WebUtility.HtmlEncode.

После всего этого формируется новая строка, состоящая из тега Paragraph с соответствующими параметрами и содержащего в себе сформированный ранее набор тегов. Xaml разметка готова.

var words = source.Split(' ');
var sbInsideTags = new StringBuilder();
sbInsideTags.Append(@"<Run Text=""");
foreach (var word in words)
{
    if (Regex.IsMatch(word, @"^((https?:\/\/)?(ftps?:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}(:[0-9]{1,5})?(\/\S*)?)"))
    {
        var link = word;
        link = link.Replace("&", "&amp;");
        link = Uri.EscapeUriString(link);
        sbInsideTags.Append(@"""/> <Hyperlink NavigateUri=""");
        sbInsideTags.Append(link.Contains("://") ? link : "http://" + link);
        sbInsideTags.Append(@""">");
        sbInsideTags.Append(link);
        sbInsideTags.Append(@"</Hyperlink> <Run Text=""");
    }
    else
    {
        sbInsideTags.Append(WebUtility.HtmlEncode(word));
        sbInsideTags.Append(' ');
    }
}
var sbXaml = new StringBuilder();
sbXaml.Append(@"<Paragraph xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" TextAlignment=""Left"" FontSize=""20""  FontWeight=""Normal"" FontStyle=""Normal"" FontStretch=""Normal"" >");
sbXaml.Append(sbInsideTags);
sbXaml.Append(@" ""/></Paragraph>");
return sbXaml.ToString();
Tags:
Hubs:
Total votes 6: ↑4 and ↓2 +2
Views 2.8K
Comments Comments 7

Information

Founded
Location
Россия
Website
edusty.ru
Employees
2–10 employees
Registered