Todas las respuestas aquí solo usan TextBoxo intentan implementar la selección de texto manualmente, lo que conduce a un bajo rendimiento o un comportamiento no nativo (parpadeo TextBox, no hay soporte de teclado en implementaciones manuales, etc.)
Después de horas de buscar y leer el código fuente de WPF , descubrí una forma de habilitar la selección de texto WPF nativo para los TextBlockcontroles (o realmente cualquier otro control). La mayor parte de la funcionalidad en torno a la selección de texto se implementa en la System.Windows.Documents.TextEditorclase del sistema.
Para habilitar la selección de texto para su control, debe hacer dos cosas:
Llame TextEditor.RegisterCommandHandlers()una vez para registrar controladores de eventos de clase
Crea una instancia de TextEditorpara cada instancia de tu clase y pasa la instancia subyacente de tu System.Windows.Documents.ITextContainera ella
También existe el requisito de que la Focusablepropiedad de su control esté establecida True.
¡Eso es todo! Suena fácil, pero desafortunadamente la TextEditorclase está marcada como interna. Así que tuve que escribir un envoltorio de reflexión a su alrededor:
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);
}
}
También creé un SelectableTextBlockderivado de TextBlockque toma los pasos mencionados anteriormente:
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);
}
}
Otra opción sería crear una propiedad adjunta para TextBlockhabilitar la selección de texto a pedido. En este caso, para deshabilitar la selección nuevamente, uno necesita separar un TextEditorusando el equivalente de reflexión de este código:
_editor.TextContainer.TextView = null;
_editor.OnDetach();
_editor = null;