Вот попытка решить некоторые проблемы с другими решениями:
- При использовании контекстного меню правой кнопкой мыши для вырезки / копирования / вставки выделяется весь текст, даже если вы не выбрали все это.
- При возврате из контекстного меню правой кнопкой мыши, весь текст всегда выделен.
- При возврате в приложение с Alt+ Tabвесь текст всегда выделяется.
- При попытке выделить только часть текста при первом нажатии все всегда выбирается (в отличие, например, от адресной строки Google chromes).
Код, который я написал, настраивается. Вы можете выбрать на какие действия выбрать все поведение должно происходить путем установки трех полей: только для чтения SelectOnKeybourdFocus
, SelectOnMouseLeftClick
, SelectOnMouseRightClick
.
Недостатком этого решения является то, что оно более сложное и статическое состояние сохраняется. Это кажется уродливой борьбой со стандартным поведением TextBox
контроля. Тем не менее, это работает, и весь код скрыт в классе контейнера Attached Property.
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;
}
}
Чтобы прикрепить Attached Property к a TextBox
, все, что вам нужно сделать, это добавить пространство имен xml ( xmlns
) Attached Property, а затем использовать его следующим образом:
<TextBox attachedprop:TextBoxExtensions.SelectOnFocus="True"/>
Некоторые замечания об этом решении:
- Чтобы переопределить стандартное поведение события нажатия мыши и включить выделение только части текста при первом щелчке, весь текст выделяется при событии мыши вверх.
- Мне пришлось иметь дело с тем, что он
TextBox
помнит свой выбор после того, как теряет фокус. Я фактически переопределил это поведение.
- Я должен был вспомнить, является ли нажатие кнопки мыши первым действием на
TextBox
( FirstActionIsMouseDown
статическое поле).
- Я должен был вспомнить контекстное меню, открываемое по щелчку правой кнопкой мыши (
ContextMenu
статическое поле).
Единственный побочный эффект, который я обнаружил, это когда SelectOnMouseRightClick
это правда. Иногда контекстное меню, вызываемое правой кнопкой мыши, мерцает, когда его открывают, а щелчок правой кнопкой мыши на пустом месте TextBox
не делает «выбрать все».