using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; namespace SelectableTextBlock { public class TextBlockSelect : TextBlock { TextPointer startpoz; TextPointer endpoz; MenuItem copyMenu; MenuItem selectAllMenu; public TextRange Selection { get; private set; } public bool HasSelection { get { return Selection != null && !Selection.IsEmpty; } } #region SelectionBrush public static readonly DependencyProperty SelectionBrushProperty = DependencyProperty.Register("SelectionBrush", typeof(Brush), typeof(TextBlockSelect), new FrameworkPropertyMetadata(Brushes.Orange)); public Brush SelectionBrush { get { return (Brush)GetValue(SelectionBrushProperty); } set { SetValue(SelectionBrushProperty, value); } } #endregion public static readonly DependencyProperty Text2Property = DependencyProperty.Register("Text2", typeof(string), typeof(TextBlockSelect), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnText2PropertyChanged))); public string Text2 { get { return (string)GetValue(Text2Property); } set { SetValue(Text2Property, value); } } static void OnText2PropertyChanged(object sender, DependencyPropertyChangedEventArgs args) { if (sender != null && sender is TextBlockSelect) { TextBlockSelect view = (TextBlockSelect)sender; if (args != null && args.NewValue != null) { string value = args.NewValue.ToString(); if (string.IsNullOrWhiteSpace(value)) { view.Text = ""; view.Inlines.Clear(); } else { view.AddInlines(value); } } else { view.Text = ""; view.Inlines.Clear(); } } } static void link_Click(object sender, RoutedEventArgs e) { Hyperlink link = sender as Hyperlink; Process.Start(new ProcessStartInfo(link.NavigateUri.AbsoluteUri)); //throw new NotImplementedException(); } public void AddInlines(string value) { Regex urlregex = new Regex(@"((http|ftp|https)://)(([a-zA-Z0-9._-]+.[a-zA-Z]{2,6})|([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9&%_./-~-]*)?", RegexOptions.IgnoreCase | RegexOptions.Compiled); var ss = urlregex.Matches(value); List<Tuple<int, int, string>> urlList = new List<Tuple<int, int, string>>(); foreach (Match item in ss) { Tuple<int, int, string> urlIndex = new Tuple<int, int, string>(value.IndexOf(item.Value), item.Value.Length, item.Value); urlList.Add(urlIndex); } if (urlList.Count > 0) { //urlList.Sort(); for (int i = 0; i < urlList.Count; i++) { if (i == 0) { string startValue = value.Substring(0, urlList[0].Item1); if (string.IsNullOrEmpty(startValue)) startValue = " "; this.Inlines.Add(new Run() { Text = startValue }); AddHyperlink(urlList[0].Item3); } else { int stratIndex = urlList[i - 1].Item1 + urlList[i - 1].Item2; this.Inlines.Add(new Run() { Text = value.Substring(stratIndex, urlList[i].Item1 - stratIndex) }); AddHyperlink(urlList[i].Item3); } if (i == urlList.Count - 1) { string endValue = value.Substring(urlList[i].Item1 + urlList[i].Item2); if (string.IsNullOrEmpty(endValue)) endValue = " "; this.Inlines.Add(new Run() { Text = endValue }); } } } else { this.Inlines.Clear(); this.Text = value; } } private void AddHyperlink(string value) { try { Hyperlink link = new Hyperlink(); link.NavigateUri = new Uri(value); link.Click += link_Click; link.Inlines.Add(new Run() { Text = value }); this.Inlines.Add(link); } catch { this.Inlines.Add(new Run() { Text = value }); } } public TextBlockSelect() { Focusable = true; //InitMenu(); } void InitMenu() { var contextMenu = new ContextMenu(); ContextMenu = contextMenu; copyMenu = new MenuItem(); copyMenu.Header = "复制"; copyMenu.InputGestureText = "Ctrl + C"; copyMenu.Click += (ss, ee) => { Copy(); }; contextMenu.Items.Add(copyMenu); selectAllMenu = new MenuItem(); selectAllMenu.Header = "全选"; selectAllMenu.InputGestureText = "Ctrl + A"; selectAllMenu.Click += (ss, ee) => { SelectAll(); }; contextMenu.Items.Add(selectAllMenu); ContextMenuOpening += contextMenu_ContextMenuOpening; } void contextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e) { copyMenu.IsEnabled = HasSelection; } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { Keyboard.Focus(this); ReleaseMouseCapture(); base.OnMouseLeftButtonUp(e); } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { var point = e.GetPosition(this); startpoz = GetPositionFromPoint(point, true); CaptureMouse(); base.OnMouseLeftButtonDown(e); } protected override void OnMouseMove(MouseEventArgs e) { if (IsMouseCaptured) { var point = e.GetPosition(this); endpoz = GetPositionFromPoint(point, true); ClearSelection(); Selection = new TextRange(startpoz, endpoz); Selection.ApplyPropertyValue(TextElement.BackgroundProperty, SelectionBrush); CommandManager.InvalidateRequerySuggested(); OnSelectionChanged(EventArgs.Empty); } base.OnMouseMove(e); } protected override void OnKeyUp(KeyEventArgs e) { if (Keyboard.Modifiers == ModifierKeys.Control) { if (e.Key == Key.C) Copy(); else if (e.Key == Key.A) SelectAll(); } base.OnKeyUp(e); } protected override void OnLostFocus(RoutedEventArgs e) { ClearSelection(); //base.OnLostFocus(e); } public bool Copy() { if (HasSelection) { Clipboard.SetDataObject(Selection.Text); return true; } return false; } public void ClearSelection() { var contentRange = new TextRange(ContentStart, ContentEnd); contentRange.ApplyPropertyValue(TextElement.BackgroundProperty, null); Selection = null; } public void SelectAll() { Selection = new TextRange(ContentStart, ContentEnd); Selection.ApplyPropertyValue(TextElement.BackgroundProperty, SelectionBrush); } public event EventHandler SelectionChanged; protected virtual void OnSelectionChanged(EventArgs e) { var handler = this.SelectionChanged; if (handler != null) handler(this, e); } } }
在 WPF: 可以点击选择和复制文本的TextBlock 的基础上添加了对Url的识别。