Делать ссылки кликабельными в тексте

320
02 мая 2017, 03:51

Использую ListView для отображения логов. Иногда в логе бывают ссылки (напимер, "Некорректная ссылка: http://example.com/"), хотелось бы, чтобы на них просто нажать можно было и они открылись в обычном браузере.

Можно ли это сделать автоматически или придётся парсить ручками и конвертировать в комбинацию Run и Hyperlink?

Answer 1

Если ссылки могут встречаться в любом месте лога, то можно написать регулярное выражение, которое вытащит нужную ссылку или ссылки. Сформировать на основе полученной информации модель представления. А потом написать шаблон для элемента ListView, который будет использовать HyperLink.

Answer 2

Используя реализацию отсюда и regex отсюда (правда, я добавил в свой шаблон запятые, как разделитель, в логах так встречается, к сожалению) получилась вот такая реализация.

  public class TextBlockWithHyperlinks : TextBlock
  {
    private static readonly Regex HyperlinkRegex = new Regex(@"(https?|ftp):\/\/[^\s/$.?#].[^\s,]*");
    static TextBlockWithHyperlinks()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBlockWithHyperlinks), new FrameworkPropertyMetadata(typeof(TextBlockWithHyperlinks)));
    }
    public TextBlockWithHyperlinks()
    {
      TargetUpdated += (s, args) => Dispatcher.BeginInvoke(DispatcherPriority.ContextIdle, new Action(ParseHyperlinks));
    }
    private void ParseHyperlinks()
    {
      var style = HyperlinkStyle;
      var text = Text;
      var matches = HyperlinkRegex.Matches(text);
      if (matches.Count == 0)
        return;
      Inlines.Clear();
      var lastIndex = 0;
      foreach (Match match in matches)
      {
        Inlines.Add(text.Substring(lastIndex, match.Index - lastIndex));
        lastIndex = match.Index + match.Length;
        var run = new Run(match.Value) { Style = style };
        run.MouseDown += RunOnMouseDown;
        Inlines.Add(run);
      }
      Inlines.Add(text.Substring(lastIndex));
    }
    private void RunOnMouseDown(object sender, MouseButtonEventArgs args)
    {
      var run = sender as Run;
      if (run == null)
        return;
      OnHyperlinkPressed(new HyperlinkPressedEventArgs(run.Text));
    }
    public event EventHandler<HyperlinkPressedEventArgs> HyperlinkPressed;
    public void OnHyperlinkPressed(HyperlinkPressedEventArgs args)
    {
      var handler = HyperlinkPressed;
      handler?.Invoke(this, args);
    }
    public static readonly DependencyProperty HyperlinkStyleProperty = DependencyProperty.Register("HyperlinkStyle",
                                                                                              typeof(Style),
                                                                                              typeof(TextBlockWithHyperlinks));
    public Style HyperlinkStyle
    {
      get { return (Style)GetValue(HyperlinkStyleProperty); }
      set { SetValue(HyperlinkStyleProperty, value); }
    }
  }
  public class HyperlinkPressedEventArgs : EventArgs
  {
    public readonly Uri Hyperlink;
    public HyperlinkPressedEventArgs(Uri hyperlink)
    {
      Hyperlink = hyperlink;
    }
    public HyperlinkPressedEventArgs(string hyperlink) : this(new Uri(hyperlink))
    {
    }
  }

В разметку вставляется легко, есть только одно но - всегда надо включать NotifyOnTargetUpdated.

          <local:TextBlockWithHyperlinks HyperlinkPressed="TextBlockWithLinks_OnHyperlinkPressed" 
                                    HyperlinkStyle="{StaticResource HyperlinkStyle}"
                                    Text="{Binding FormattedMessage, NotifyOnTargetUpdated=True}"/>

Стиль простой, подчеркивание и синий цвет:

<Style x:Key="HyperlinkStyle" TargetType="{x:Type Run}" >
  <Style.Setters>
    <Setter Property="Foreground" Value="Blue"/>
  </Style.Setters>
  <Style.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
      <Setter Property="TextDecorations" Value="Underline"/>
      <Setter Property="Cursor" Value="Hand"/>
    </Trigger>
  </Style.Triggers>
</Style>

Обработчик ссылок по идее надо бы делать с защитой от опасных операций, но пока так:

private void TextBlockWithLinks_OnHyperlinkPressed(object sender, HyperlinkPressedEventArgs args)
{
  System.Diagnostics.Process.Start(args.Hyperlink.OriginalString);
}
READ ALSO
Рекурсия. Как происходит вход в условие

Рекурсия. Как происходит вход в условие

Имеется такая функция, которая вызывает сама себя, то есть рекурсивнаяДопустим n = 5

248
Как сделать простейший клиент для базы данных MSSQLS Express в Windows Forms приложении на C#

Как сделать простейший клиент для базы данных MSSQLS Express в Windows Forms приложении на C#

Сейчас я только понял как добавить в источники данных БДДальше не могу понять, как поступить дальше

254
Не виден picterbox на форме?

Не виден picterbox на форме?

Ругается на picturebox1 - хотя на форме он естьВо время выполнения NullReferenceException

227
WCF Callback for UDP

WCF Callback for UDP

Как в WCF, получая и отправляя данные по UDP использовать Callback?

226