Đây là một nỗ lực để giải quyết một số vấn đề với các giải pháp khác:
- Sử dụng menu ngữ cảnh nhấp chuột phải để cắt / sao chép / quá khứ sẽ chọn tất cả văn bản ngay cả khi bạn không chọn tất cả.
- Khi trở về từ menu ngữ cảnh nhấp chuột phải, tất cả văn bản luôn được chọn.
- Khi trở về ứng dụng có dấu Alt+ Tab, tất cả văn bản luôn được chọn.
- Khi cố gắng chỉ chọn một phần văn bản trong lần nhấp đầu tiên, tất cả luôn được chọn (không giống như thanh địa chỉ Google chromes chẳng hạn).
Mã tôi đã viết là cấu hình. Bạn có thể chọn vào những gì hành động chọn tất cả các hành vi xảy ra bằng cách thiết lập ba lĩnh vực readonly: SelectOnKeybourdFocus
, SelectOnMouseLeftClick
, SelectOnMouseRightClick
.
Nhược điểm của giải pháp này là trạng thái tĩnh và phức tạp hơn được lưu trữ. Nó có vẻ như một cuộc đấu tranh xấu xí với hành vi mặc định của TextBox
kiểm soát. Tuy nhiên, nó vẫn hoạt động và tất cả các mã được ẩn trong lớp thùng chứa Thuộc tính đính kèm.
public static class TextBoxExtensions
{
// Configuration fields to choose on what actions the select all behavior should occur.
static readonly bool SelectOnKeybourdFocus = true;
static readonly bool SelectOnMouseLeftClick = true;
static readonly bool SelectOnMouseRightClick = true;
// Remembers a right click context menu that is opened
static ContextMenu ContextMenu = null;
// Remembers if the first action on the TextBox is mouse down
static bool FirstActionIsMouseDown = false;
public static readonly DependencyProperty SelectOnFocusProperty =
DependencyProperty.RegisterAttached("SelectOnFocus", typeof(bool), typeof(TextBoxExtensions), new PropertyMetadata(false, new PropertyChangedCallback(OnSelectOnFocusChanged)));
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static bool GetSelectOnFocus(DependencyObject obj)
{
return (bool)obj.GetValue(SelectOnFocusProperty);
}
public static void SetSelectOnFocus(DependencyObject obj, bool value)
{
obj.SetValue(SelectOnFocusProperty, value);
}
private static void OnSelectOnFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox)) return;
if (GetSelectOnFocus(textBox))
{
// Register events
textBox.PreviewMouseDown += TextBox_PreviewMouseDown;
textBox.PreviewMouseUp += TextBox_PreviewMouseUp;
textBox.GotKeyboardFocus += TextBox_GotKeyboardFocus;
textBox.LostKeyboardFocus += TextBox_LostKeyboardFocus;
}
else
{
// Unregister events
textBox.PreviewMouseDown -= TextBox_PreviewMouseDown;
textBox.PreviewMouseUp -= TextBox_PreviewMouseUp;
textBox.GotKeyboardFocus -= TextBox_GotKeyboardFocus;
textBox.LostKeyboardFocus -= TextBox_LostKeyboardFocus;
}
}
private static void TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// If mouse clicked and focus was not in text box, remember this is the first click.
// This will enable to prevent select all when the text box gets the keyboard focus
// right after the mouse down event.
if (!textBox.IsKeyboardFocusWithin)
{
FirstActionIsMouseDown = true;
}
}
private static void TextBox_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// Select all only if:
// 1) SelectOnMouseLeftClick/SelectOnMouseRightClick is true and left/right button was clicked
// 3) This is the first click
// 4) No text is selected
if (((SelectOnMouseLeftClick && e.ChangedButton == MouseButton.Left) ||
(SelectOnMouseRightClick && e.ChangedButton == MouseButton.Right)) &&
FirstActionIsMouseDown &&
string.IsNullOrEmpty(textBox.SelectedText))
{
textBox.SelectAll();
}
// It is not the first click
FirstActionIsMouseDown = false;
}
private static void TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// Select all only if:
// 1) SelectOnKeybourdFocus is true
// 2) Focus was not previously out of the application (e.OldFocus != null)
// 3) The mouse was pressed down for the first after on the text box
// 4) Focus was not previously in the context menu
if (SelectOnKeybourdFocus &&
e.OldFocus != null &&
!FirstActionIsMouseDown &&
!IsObjectInObjectTree(e.OldFocus as DependencyObject, ContextMenu))
{
textBox.SelectAll();
}
// Forget ContextMenu
ContextMenu = null;
}
private static void TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(sender is TextBox textBox)) return;
// Remember ContextMenu (if opened)
ContextMenu = e.NewFocus as ContextMenu;
// Forget selection when focus is lost if:
// 1) Focus is still in the application
// 2) The context menu was not opened
if (e.NewFocus != null
&& ContextMenu == null)
{
textBox.SelectionLength = 0;
}
}
// Helper function to look if a DependencyObject is contained in the visual tree of another object
private static bool IsObjectInObjectTree(DependencyObject searchInObject, DependencyObject compireToObject)
{
while (searchInObject != null && searchInObject != compireToObject)
{
searchInObject = VisualTreeHelper.GetParent(searchInObject);
}
return searchInObject != null;
}
}
Để tùy chỉnh Thuộc tính được đính kèm vào a TextBox
, tất cả những gì bạn cần làm là thêm không gian tên xml ( xmlns
) của Thuộc tính được đính kèm và sau đó sử dụng nó như sau:
<TextBox attachedprop:TextBoxExtensions.SelectOnFocus="True"/>
Một số lưu ý về giải pháp này:
- Để ghi đè hành vi mặc định của sự kiện chuột xuống và cho phép chỉ chọn một phần văn bản trong lần nhấp đầu tiên, tất cả văn bản được chọn trong sự kiện chuột lên.
- Tôi đã phải đối phó với thực tế là những người còn
TextBox
nhớ lựa chọn của nó sau khi nó mất tập trung. Tôi thực sự đã ghi đè hành vi này.
- Tôi phải nhớ nếu một nút chuột xuống là hành động đầu tiên trên
TextBox
( FirstActionIsMouseDown
trường tĩnh).
- Tôi phải nhớ menu ngữ cảnh được mở bằng một cú nhấp chuột phải (
ContextMenu
trường tĩnh).
Tác dụng phụ duy nhất tôi tìm thấy là khi nào SelectOnMouseRightClick
là đúng. Đôi khi, trình đơn ngữ cảnh nhấp chuột phải nhấp nháy khi mở và nhấp chuột phải vào chỗ trống, TextBox
không "chọn tất cả".