Wszystkie odpowiedzi tutaj używają po prostu TextBoxlub próbują zaimplementować zaznaczenie tekstu ręcznie, co prowadzi do niskiej wydajności lub nienatywnego zachowania (mruganie okiem TextBox, brak obsługi klawiatury w implementacjach ręcznych itp.)
Po wielu godzinach przekopywania się i czytaniu kodu źródłowego WPF , zamiast tego odkryłem sposób na włączenie natywnego zaznaczania tekstu WPF dla TextBlockformantów (lub naprawdę innych formantów). Większość funkcji związanych z zaznaczaniem tekstu jest zaimplementowana w System.Windows.Documents.TextEditorklasie systemowej.
Aby włączyć zaznaczanie tekstu dla swojej kontroli, musisz zrobić dwie rzeczy:
Zadzwoń TextEditor.RegisterCommandHandlers()raz, aby zarejestrować procedury obsługi zdarzeń klasowych
Tworzenie instancji TextEditordla każdej instancji klasy i przekazać podstawową otworzonym System.Windows.Documents.ITextContainerdo niego
Istnieje również wymóg, aby Focusablewłaściwość kontroli była ustawiona na True.
To jest to! Brzmi łatwo, ale niestety TextEditorklasa jest oznaczona jako wewnętrzna. Musiałem więc napisać wokół niego opakowanie refleksyjne:
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);
}
}
Stworzyłem również SelectableTextBlockpochodną, TextBlockktóra wykonuje czynności wymienione powyżej:
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);
}
}
Inną opcją byłoby utworzenie dołączonej właściwości TextBlockumożliwiającej zaznaczanie tekstu na żądanie. W takim przypadku, aby ponownie wyłączyć zaznaczenie, należy odłączyć a TextEditorza pomocą równoważnika odbicia tego kodu:
_editor.TextContainer.TextView = null;
_editor.OnDetach();
_editor = null;