Использование гиперссылок внутри текста в Silverlight 3

Вставить гиперссылку внутри какого-то текста – совершенно обычная задача для web-приложений. Очень часто мы делаем это при создании HTML документов. К сожалению, элементы управления в Silverlight не позволяют вставлять гиперссылки внутри текста. Давайте рассмотрим пример того, как можно обойти это ограничение.

Если быть до конца честным, то в Silverlight 3 есть элемент управления HyperlinkButton, который все-таки позволяет вставлять гиперссылки в приложения Silverlight. Проблема этого контрола заключается в том, что по умолчанию невозможно вставить его внутри текста. Одним из решением данной проблемы может быть построение нового элемента управления следующим образом.

Во-первых, разобьем наше предложение на слова. Разделителем может быть, например, пробел и знаки препинания. После этого необходимо будет понять какие из слов являются гиперссылками. Для этих целей можно использовать, например, регулярные выражения.

Во-вторых, для каждого слова создадим собственный элемент управления. Если слово не является гиперссылкой, то создадим для него элемент управления TextBlock. Если слово является гиперссылкой, то создадим для него элемент управления HyperlinkButton.

В третьих, разместим все наши элементы управления в контейнере WrapPanel.

Собственный элемент управления можно реализовать несколькими способами. В качестве базы можно использовать UserControl или какие-то другие типы контролов. Однако, удобнее в качестве базового объекта выбрать WrapPanel, т.к. никакой дополнительной логики в нашем случае не требуется.

Давайте рассмотрим процесс построения такого элемента управления по шагам.

Шаг 1: Создадим элемент управления и унаследуем его от WrapPanel.

public class LinkTextBlock : WrapPanel
{
  //...
}

Шаг 2: Создадим свойство, которое содержит текст.

public class LinkTextBlock : WrapPanel
{
  public static DependencyProperty TextProperty;

  static LinkTextBlock()
  {
    TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(LinkTextBlock), new PropertyMetadata(String.Empty, TextChangedCallback));
    // ...
  }

  private static void TextChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    var control = (LinkTextBlock)d;
    control.BuildControls();
  }

  public string Text
  {
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value); }
  }

  // ...
}

Шаг 3: Определим метод для разбивки предложения на слова. Будем запускать этот метод всякий раз когда обновляется текст (метод TextChangedCallback).

private void BuildControls()
{
  Children.Clear();

  foreach (UIElement control in from word in Text.Split(' ')
                                select BuildWordControl(word))
  {
    Children.Add(control);
  }
}

Шаг 4: Определяем метод для построения элементов управления для каждого слова.

private UIElement BuildWordControl(string text)
{
  UIElement result;
  string url, displayText;

  if ((CheckLinkMethod != null) && (CheckLinkMethod(text, out url, out displayText) == true))
  {
    Uri navigateUri = new Uri(..);

    result = new HyperlinkButton() { Content = displayText, Tag = url, NavigateUri = navigateUri };
    ((HyperlinkButton)result).Click += delegate(object sender, RoutedEventArgs e)
                                            {
                                                // do something
                                            };
  }
  else
  {
    result = new TextBlock() { Text = text };
  }

  return result;
}

На этом элемент управления готов к употреблению :)

В финальном коде я вставил делегаты, которые позволяют задавать логику определения гиперссылок, стили для слов, подсказки и другие вспомогательные элементы. Скачать исходный код проекта можно по ссылке ниже.