Berikut ini adalah upaya untuk memecahkan beberapa masalah dengan solusi lain:
- Menggunakan menu konteks klik kanan untuk cut / copy / past memilih semua teks bahkan jika Anda tidak memilih semuanya.
- Ketika kembali dari menu konteks klik kanan, semua teks selalu dipilih.
- Ketika kembali ke aplikasi dengan Alt+Tab , semua teks selalu dipilih.
- Ketika mencoba untuk memilih hanya sebagian dari teks pada klik pertama, semua selalu dipilih (tidak seperti bilah alamat Google misalnya).
Kode yang saya tulis dapat dikonfigurasi. Anda dapat memilih apa tindakan pilih semua perilaku harus terjadi dengan menetapkan tiga bidang dibaca: SelectOnKeybourdFocus
, SelectOnMouseLeftClick
,SelectOnMouseRightClick
.
Kelemahan dari solusi ini adalah lebih rumit dan keadaan statis disimpan. Sepertinya perjuangan buruk dengan perilaku default dari TextBox
kontrol. Namun, ia berfungsi dan semua kode disembunyikan di kelas kontainer Properti Terlampir.
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;
}
}
Untuk memasang Properti Terlampir ke a TextBox
, semua yang perlu Anda lakukan adalah menambahkan namespace xml ( xmlns
) dari Properti Terlampir dan kemudian menggunakannya seperti ini:
<TextBox attachedprop:TextBoxExtensions.SelectOnFocus="True"/>
Beberapa catatan tentang solusi ini:
- Untuk mengganti perilaku default acara mouse-down dan mengaktifkan hanya memilih bagian dari teks pada klik pertama, semua teks dipilih pada acara mouse-up.
- Aku harus berurusan dengan fakta
TextBox
mengingat seleksi setelah kehilangan fokus. Saya sebenarnya telah menimpa perilaku ini.
- Saya harus ingat jika tombol mouse di bawah adalah tindakan pertama di Internet
TextBox
(FirstActionIsMouseDown
bidang statis).
- Saya harus mengingat menu konteks yang dibuka oleh klik kanan (
ContextMenu
bidang statis).
Satu-satunya efek samping yang saya temukan adalah kapan SelectOnMouseRightClick
benar. Kadang-kadang menu konteks klik kanan berkedip ketika itu dibuka dan mengklik kanan pada kosong di TextBox
tidak melakukan "pilih semua".