Tất cả các câu trả lời ở đây chỉ là sử dụng TextBox
hoặc cố gắng thực hiện lựa chọn văn bản theo cách thủ công, điều này dẫn đến hiệu suất kém hoặc hành vi không bản địa (nhấp nháy TextBox
, không hỗ trợ bàn phím khi triển khai thủ công, v.v.)
Sau nhiều giờ tìm hiểu và đọc mã nguồn WPF , thay vào đó tôi phát hiện ra một cách cho phép lựa chọn văn bản WPF gốc cho các TextBlock
điều khiển (hoặc thực sự là bất kỳ điều khiển nào khác). Hầu hết các chức năng xung quanh lựa chọn văn bản được thực hiện trong System.Windows.Documents.TextEditor
lớp hệ thống.
Để cho phép lựa chọn văn bản cho điều khiển của bạn, bạn cần thực hiện hai điều:
Gọi TextEditor.RegisterCommandHandlers()
một lần để đăng ký xử lý sự kiện lớp
Tạo một thể hiện TextEditor
cho từng thể hiện của lớp của bạn và truyền thể hiện cơ bản của bạn System.Windows.Documents.ITextContainer
cho nó
Ngoài ra còn có một yêu cầu là Focusable
tài sản kiểm soát của bạn được đặt thành True
.
Đây là nó! Nghe có vẻ dễ, nhưng tiếc TextEditor
là lớp được đánh dấu là nội bộ. Vì vậy, tôi đã phải viết một trình bao bọc phản chiếu xung quanh nó:
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);
}
}
Tôi cũng đã tạo ra một SelectableTextBlock
dẫn xuất từ TextBlock
đó thực hiện các bước được ghi chú ở trên:
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);
}
}
Một tùy chọn khác là tạo một thuộc tính đính kèm TextBlock
để cho phép lựa chọn văn bản theo yêu cầu. Trong trường hợp này, để vô hiệu hóa lựa chọn một lần nữa, người ta cần tách a TextEditor
bằng cách sử dụng phản xạ tương đương với mã này:
_editor.TextContainer.TextView = null;
_editor.OnDetach();
_editor = null;