这是尝试解决其他解决方案中的一些问题的尝试:
- 使用右键单击上下文菜单进行剪切/复制/粘贴会选择所有文本,即使您没有全部选择。
- 从右键单击上下文菜单返回时,始终选中所有文本。
- 使用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;
}
}
要将附加属性附加到TextBox
,您需要做的就是添加xmlns
附加属性的xml名称空间(),然后像这样使用它:
<TextBox attachedprop:TextBoxExtensions.SelectOnFocus="True"/>
有关此解决方案的一些注意事项:
- 要覆盖鼠标按下事件的默认行为并启用第一次单击时仅选择部分文本的功能,请在鼠标按下事件时选择所有文本。
- 我不得不面对这样一个事实,那就是
TextBox
它在失去焦点之后会记住它的选择。我实际上已经覆盖了这种行为。
- 我必须记住,按下鼠标是否是
TextBox
(FirstActionIsMouseDown
静态字段)。
- 我必须记住右键单击打开的上下文菜单(
ContextMenu
静态字段)。
我发现的唯一副作用是when SelectOnMouseRightClick
是true。有时,右键单击上下文菜单在打开时闪烁,而在空白处单击鼠标右键TextBox
则不会“全选”。