Можно ли выбрать текстовый блок WPF?

Я хочу, чтобы текст отображался в Witty , клиенте с открытым исходным кодом Twitter, который можно выбрать. В настоящее время он отображается с использованием настраиваемого текстового блока. Мне нужно использовать TextBlock, потому что я работаю с встроенными строками textblock для отображения и форматирования имени @us и ссылок как гиперссылок. Частым запросом является возможность вставить текст в текст. Для этого мне нужно сделать TextBlock доступным.

Я попытался заставить его работать, отображая текст, используя текстовый блок только для чтения, который выглядит как текстовый блок, но это не будет работать в моем случае, потому что TextBox не имеет встроенных строк. Другими словами, я не могу стилизовать или форматировать текст внутри TextBox отдельно, как я могу, с помощью TextBlock.

Есть идеи?

     

    Все ответы здесь просто используют TextBox или пытаются вручную выполнить выбор текста, что приводит к плохой производительности или нелогичному поведению (мигающий кареток в TextBox , отсутствие поддержки клавиатуры в ручных реализациях и т. Д.),

    После нескольких часов работы и чтения исходного кода WPF я вместо этого обнаружил способ включения собственного текста текста WPF для элементов управления TextBlock (или действительно любых других элементов управления). Большая часть функциональности вокруг выбора текста реализована в системном classе System.Windows.Documents.TextEditor .

    Чтобы включить выбор текста для вашего контроля, вам нужно сделать две вещи:

    1. Вызовите TextEditor.RegisterCommandHandlers() один раз, чтобы зарегистрировать обработчики событий classа

    2. Создайте экземпляр TextEditor для каждого экземпляра вашего classа и передайте ему базовый экземпляр вашего System.Windows.Documents.ITextContainer

    Также необходимо, чтобы свойство Focusable вашего Focusable управления было установлено значение True .

    Это оно! Звучит просто, но, к сожалению, class TextEditor отмечен как внутренний. Поэтому мне пришлось написать вокруг него обертку для размышлений:

     class TextEditorWrapper { private static readonly Type TextEditorType = Type.GetType("System.Windows.Documents.TextEditor, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); private static readonly PropertyInfo IsReadOnlyProp = TextEditorType.GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly PropertyInfo TextViewProp = TextEditorType.GetProperty("TextView", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly MethodInfo RegisterMethod = TextEditorType.GetMethod("RegisterCommandHandlers", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(Type), typeof(bool), typeof(bool), typeof(bool) }, null); private static readonly Type TextContainerType = Type.GetType("System.Windows.Documents.ITextContainer, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); private static readonly PropertyInfo TextContainerTextViewProp = TextContainerType.GetProperty("TextView"); private static readonly PropertyInfo TextContainerProp = typeof(TextBlock).GetProperty("TextContainer", BindingFlags.Instance | BindingFlags.NonPublic); public static void RegisterCommandHandlers(Type controlType, bool acceptsRichContent, bool readOnly, bool registerEventListeners) { RegisterMethod.Invoke(null, new object[] { controlType, acceptsRichContent, readOnly, registerEventListeners }); } public static TextEditorWrapper CreateFor(TextBlock tb) { var textContainer = TextContainerProp.GetValue(tb); var editor = new TextEditorWrapper(textContainer, tb, false); IsReadOnlyProp.SetValue(editor._editor, true); TextViewProp.SetValue(editor._editor, TextContainerTextViewProp.GetValue(textContainer)); return editor; } private readonly object _editor; public TextEditorWrapper(object textContainer, FrameworkElement uiScope, bool isUndoEnabled) { _editor = Activator.CreateInstance(TextEditorType, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.CreateInstance, null, new[] { textContainer, uiScope, isUndoEnabled }, null); } } 

    Я также создал SelectableTextBlock полученный из TextBlock который выполняет описанные выше шаги:

     public class SelectableTextBlock : TextBlock { static SelectableTextBlock() { FocusableProperty.OverrideMetadata(typeof(SelectableTextBlock), new FrameworkPropertyMetadata(true)); TextEditorWrapper.RegisterCommandHandlers(typeof(SelectableTextBlock), true, true, true); // remove the focus rectangle around the control FocusVisualStyleProperty.OverrideMetadata(typeof(SelectableTextBlock), new FrameworkPropertyMetadata((object)null)); } private readonly TextEditorWrapper _editor; public SelectableTextBlock() { _editor = TextEditorWrapper.CreateFor(this); } } 

    Другой вариант – создать прикрепленное свойство TextBlock для включения выбора текста по требованию. В этом случае, чтобы отключить выбор снова, нужно отсоединить TextEditor , используя эквивалент отражения этого кода:

     _editor.TextContainer.TextView = null; _editor.OnDetach(); _editor = null; 

    Я не смог найти ни одного примера ответа на этот вопрос. Все ответы использовали текстовое поле или RichTextbox. Мне нужно решение, которое позволило мне использовать TextBlock, и это решение, которое я создал.

    Я считаю, что правильный способ сделать это – расширить class TextBlock. Это код, который я использовал для расширения classа TextBlock, чтобы я мог выбрать текст и скопировать его в буфер обмена. «sdo» – это ссылка пространства имен, которую я использовал в WPF.

    WPF Использование расширенного classа:

     xmlns:sdo="clr-namespace:iFaceCaseMain"  

    Код для расширенного classа:

     public partial class TextBlockMoo : TextBlock { TextPointer StartSelectPosition; TextPointer EndSelectPosition; public String SelectedText = ""; public delegate void TextSelectedHandler(string SelectedText); public event TextSelectedHandler TextSelected; protected override void OnMouseDown(MouseButtonEventArgs e) { base.OnMouseDown(e); Point mouseDownPoint = e.GetPosition(this); StartSelectPosition = this.GetPositionFromPoint(mouseDownPoint, true); } protected override void OnMouseUp(MouseButtonEventArgs e) { base.OnMouseUp(e); Point mouseUpPoint = e.GetPosition(this); EndSelectPosition = this.GetPositionFromPoint(mouseUpPoint, true); TextRange otr = new TextRange(this.ContentStart, this.ContentEnd); otr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.GreenYellow)); TextRange ntr = new TextRange(StartSelectPosition, EndSelectPosition); ntr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.White)); SelectedText = ntr.Text; if (!(TextSelected == null)) { TextSelected(SelectedText); } } } 

    Пример кода windows:

      public ucExample(IInstanceHost host, ref String WindowTitle, String ApplicationID, String Parameters) { InitializeComponent(); /*Used to add selected text to clipboard*/ this.txtResults.TextSelected += txtResults_TextSelected; } void txtResults_TextSelected(string SelectedText) { Clipboard.SetText(SelectedText); } 

    Создайте ControlTemplate для TextBlock и поместите TextBox внутри с набором свойств readonly. Или просто используйте TextBox и сделайте его только для чтения, затем вы можете изменить TextBox.Style, чтобы он выглядел как TextBlock.

    Примените этот стиль к своему TextBox, и это все (вдохновлено этой статьей ):

      

    Я не уверен, что вы можете сделать выбор TextBlock, но другой вариант – использовать RichTextBox – это как TextBox, как вы предлагали, но поддерживает нужное форматирование.

    Согласно Windows Dev Center :

    Свойство TextBlock.IsTextSelectionEnabled

    [Обновлено для приложений UWP в Windows 10. Для статей Windows 8.x см. Архив ]

    Возвращает или задает значение, указывающее, включен ли выбор текста в TextBlock , либо через действие пользователя, либо вызывающий API, связанный с выбором.

    TextBlock не имеет шаблона. Поэтому для достижения этого нам нужно использовать TextBox, стиль которого изменился, чтобы вести себя как textBlock.

      

    Существует альтернативное решение, которое может быть адаптировано к RichTextBox, выпущенному в этом сообщении в блоге, – оно использовало триггер для замены шаблона управления, когда использование зависает над элементом управления, – должно помочь в производительности

    Хотя вопрос действительно говорит «Выбираемый», я считаю, что преднамеренные результаты – это получить текст в буфер обмена. Это можно легко и элегантно достичь, добавив контекстное меню и пункт меню, который называется копией, которая помещает значение свойства Textblock Text в буфер обмена. Просто идея в любом случае.

     new TextBox { Text = text, TextAlignment = TextAlignment.Center, TextWrapping = TextWrapping.Wrap, IsReadOnly = true, Background = Brushes.Transparent, BorderThickness = new Thickness() { Top = 0, Bottom = 0, Left = 0, Right = 0 } };
    new TextBox { Text = text, TextAlignment = TextAlignment.Center, TextWrapping = TextWrapping.Wrap, IsReadOnly = true, Background = Brushes.Transparent, BorderThickness = new Thickness() { Top = 0, Bottom = 0, Left = 0, Right = 0 } }; 

    Я реализовал SelectableTextBlock в моей библиотеке элементов управления opensource. Вы можете использовать его следующим образом:

      
     Really nice and easy solution, exactly what I wanted ! 

    Я приношу небольшие изменения

     public class TextBlockMoo : TextBlock { public String SelectedText = ""; public delegate void TextSelectedHandler(string SelectedText); public event TextSelectedHandler OnTextSelected; protected void RaiseEvent() { if (OnTextSelected != null){OnTextSelected(SelectedText);} } TextPointer StartSelectPosition; TextPointer EndSelectPosition; Brush _saveForeGroundBrush; Brush _saveBackGroundBrush; TextRange _ntr = null; protected override void OnMouseDown(MouseButtonEventArgs e) { base.OnMouseDown(e); if (_ntr!=null) { _ntr.ApplyPropertyValue(TextElement.ForegroundProperty, _saveForeGroundBrush); _ntr.ApplyPropertyValue(TextElement.BackgroundProperty, _saveBackGroundBrush); } Point mouseDownPoint = e.GetPosition(this); StartSelectPosition = this.GetPositionFromPoint(mouseDownPoint, true); } protected override void OnMouseUp(MouseButtonEventArgs e) { base.OnMouseUp(e); Point mouseUpPoint = e.GetPosition(this); EndSelectPosition = this.GetPositionFromPoint(mouseUpPoint, true); _ntr = new TextRange(StartSelectPosition, EndSelectPosition); // keep saved _saveForeGroundBrush = (Brush)_ntr.GetPropertyValue(TextElement.ForegroundProperty); _saveBackGroundBrush = (Brush)_ntr.GetPropertyValue(TextElement.BackgroundProperty); // change style _ntr.ApplyPropertyValue(TextElement.BackgroundProperty, new SolidColorBrush(Colors.Yellow)); _ntr.ApplyPropertyValue(TextElement.ForegroundProperty, new SolidColorBrush(Colors.DarkBlue)); SelectedText = _ntr.Text; } } 
    Давайте будем гением компьютера.