Voici une tentative de résoudre certains des problèmes avec d'autres solutions:
- L'utilisation du menu contextuel du clic droit pour couper / copier / coller sélectionne tout le texte même si vous ne l'avez pas tout sélectionné.
- Lorsque vous revenez du menu contextuel du clic droit, tout le texte est toujours sélectionné.
- Lorsque vous revenez à l'application avec Alt+ Tab, tout le texte est toujours sélectionné.
- Lorsque vous essayez de ne sélectionner qu'une partie du texte au premier clic, tout est toujours sélectionné (contrairement à la barre d'adresse de Google chromes par exemple).
Le code que j'ai écrit est configurable. Vous pouvez choisir sur quelles actions le sélectionner tous les comportements devraient se produire en définissant trois champs: lecture seule SelectOnKeybourdFocus
, SelectOnMouseLeftClick
, SelectOnMouseRightClick
.
L'inconvénient de cette solution est qu'elle est plus complexe et que l'état statique est stocké. Cela semble être une lutte affreuse avec le comportement par défaut du TextBox
contrôle. Pourtant, cela fonctionne et tout le code est caché dans la classe de conteneur Propriété attachée.
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;
}
}
Pour attacher la propriété attachée à un TextBox
, tout ce que vous avez à faire est d'ajouter l'espace de noms xml ( xmlns
) de la propriété attachée et de l'utiliser comme ceci:
<TextBox attachedprop:TextBoxExtensions.SelectOnFocus="True"/>
Quelques notes sur cette solution:
- Pour remplacer le comportement par défaut d'un événement souris vers le bas et activer la sélection d'une partie seulement du texte au premier clic, tout le texte est sélectionné lors de l'événement souris vers le haut.
- J'ai dû faire face au fait que le se
TextBox
souvient de sa sélection après avoir perdu le focus. J'ai en fait outrepassé ce comportement.
- Je devais me rappeler si un bouton de la souris était la première action sur le
TextBox
(FirstActionIsMouseDown
champ statique).
- Je devais me souvenir du menu contextuel ouvert par un clic droit (
ContextMenu
champ statique).
Le seul effet secondaire que j'ai trouvé, c'est quand SelectOnMouseRightClick
c'est vrai. Parfois, le menu contextuel du clic droit scintille lorsqu'il est ouvert et un clic droit sur un espace vide TextBox
ne fait pas "tout sélectionner".