TextBox元素获得焦点时,可以用SelectAll()来选择已有文字,但不能直接用SelectAll()就得到想要的效果。其中一种方式是通过PreviewMouseDown\GotFocus\LostFocus的多个事件来完成,逻辑有些繁琐。每次都写这些就造成代码重复,因此我写了一个附加属性类来解决这个问题。
本文利用创建的WPF附加属性来获得该功能:即在Text获得焦点的时候,全部选择textbox中已经存在的内容。
/// <summary>
/// 当 TextBoxBase获得焦点的时候,自动全部选择文字。附加属性为SelectAllWhenGotFocus,类型为bool.
/// </summary>
public class TextBoxAutoSelectHelper
{
public static readonly DependencyProperty SelectAllWhenGotFocusProperty =DependencyProperty.RegisterAttached("SelectAllWhenGotFocus",
typeof(bool), typeof(TextBoxAutoSelectHelper),
new FrameworkPropertyMetadata((bool)false,new PropertyChangedCallback(OnSelectAllWhenGotFocusChanged)));
public static bool GetSelectAllWhenGotFocus(TextBoxBase d)
{
return (bool)d.GetValue(SelectAllWhenGotFocusProperty);
}
public static void SetSelectAllWhenGotFocus(TextBoxBase d, bool value)
{
d.SetValue(SelectAllWhenGotFocusProperty, value);
}
private static void OnSelectAllWhenGotFocusChanged(DependencyObject dependency, DependencyPropertyChangedEventArgs e)
{
if (dependency is TextBoxBase tBox)
{
var isSelectedAllWhenGotFocus = (bool)e.NewValue;
if (isSelectedAllWhenGotFocus)
{
tBox.PreviewMouseDown += TextBoxPreviewMouseDown;
tBox.GotFocus += TextBoxOnGotFocus;
tBox.LostFocus += TextBoxOnLostFocus;
}
else
{
tBox.PreviewMouseDown -= TextBoxPreviewMouseDown;
tBox.GotFocus -= TextBoxOnGotFocus;
tBox.LostFocus -= TextBoxOnLostFocus;
}
}
}
private static void TextBoxOnGotFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBoxBase tBox)
{
tBox.SelectAll();
tBox.PreviewMouseDown -= TextBoxPreviewMouseDown;
}
}
private static void TextBoxPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (sender is TextBoxBase tBox)
{
tBox.Focus();
e.Handled = true;
}
}
private static void TextBoxOnLostFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBoxBase tBox)
{
tBox.PreviewMouseDown += TextBoxPreviewMouseDown;
}
}
}
使用方法如下:
(1)使用Style,以下是直接放在元素中的,更直接的放在窗口的资源中,或资源字典中
<TextBox Text="{Binding xxx}">
<TextBox.Style>
<Style TargetType = "TextBox" >
< Setter Property="local:TextBoxAutoSelectHelper.SelectAllWhenGotFocus" Value="True"/>
</Style>
</TextBox.Style>
</TextBox>
(2)或者直接使用元素的附加属性
<TextBox local:TextBoxAutoSelectHelper.SelectAllWhenGotFocus="True" Text="{Binding xxx}"/>
---->以上两种使用方法中的local都是代表命名空间的,具体根据实际情况即可。
补充:
(1)对于TextBox,如果之间没有获得焦点,然后要通过鼠标单击来获得焦点,其处理过程表现为:鼠标单击TextBox时,会首先产生PreviewMouseDown事件,处理完PreviewMouseDown事件后,TextBox会获得焦点并进一步产生GotFocus事件。如果在PreviewMouseDown中禁止掉事件进一步传播(e.Handle=true),那么TextBox就不会获得焦点,也不会激发GotFocus事件及其它后续事件。
(2)TextBox的先后产生的与鼠标点击有关的事件包括:PreviewMouseDown,GotFocus,PreviewMouseUp,MouseUp;对于我们想象中应该产生的MouseDown事件,测试过程中无论怎么做都没有激发MouseDown事件。建议尽量不使用PreviewMouseUp和MouseUp事件,其原因如下第(3)和第(4)条所示。
(3)对于TextBox的PreviewMouseUp事件,不建议使用,可能时BUG还是怎么回事。用了以后,整个界面点击任何位置,都只响应该TextBox的事件,其它事件都无法响应了。及时点击关闭窗口的按钮,也是仅仅响应该TextBox的事件,窗口都关闭不了。个人认为是Bug,不知道理解是否正确。
(4)对于TextBox的MouseUp事件,在测试过程中,有时是正常激发的,有时必须要点击TextBox的边框附近才会激发,而点击TextBox的内部不会激发。下图是官方文档中关于Mouse事件的说明:
(5)对于直接使用GotFocus事件来SelectAll()之所以不成功,个人理解是因为:在PreviewMouseDown事件传输过程中,TextBox先获得了焦点,并激发GotFocus事件。然后PreviewMouseDown会进一步向内传输,在此过程中TextBox还会进一步处理鼠标位置信息,并设置光标位置。正因为设置了光标的位置,这又会导致已经选择的文本被取消选择了。所以,为了获得自动选择文字的功能,就在PreviewMouseDown中主动SetFocus(注:这回激发GotFocus事件,在事件处理方法中SelectAll()),并禁止PreviewMouseDown事件进一步向TextBox的更深层次元素传播(e.Handled=true)。
(以上一些结论是根据现象推理的,由于水平有限,如有不准确,还请读者见谅)