文本框在按tab键时全选,但鼠标单击时不全选

17

假设我有一个WPF表单和几个文本框。如果您切换到文本框并且文本框中已经有一些内容,我想选择该框中的所有文本,这样打字将擦除该文本。如果您单击鼠标在框中,则可能意味着您想更改某个字母,因此在这种情况下不要突出显示所有内容。似乎很容易,但是目前为止没有一个好的解决方案。以下是我现在已经非常接近的解决方案,但还不完美。

<Style x:Key="TextBoxStyle" TargetType="TextBox">
    <EventSetter Event="GotKeyboardFocus" Handler="EventSetter_OnHandler" />
</Style>

private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
{
    TextBox txt = sender as TextBox;
    if (txt != null) txt.SelectAll();
}

因此,当该框获得键盘焦点时,它会选择所有内容,因此通过制表符到达文本框会完美地选择所有文本。但是,如果使用鼠标单击,也会调用此方法,这也会高亮显示文本,但单击后鼠标会把光标放在单击位置之后。因此,这个功能上是完美的,但仍然让我感到困扰的是,当鼠标点击时,它会闪烁并选择所有内容。是否有更好的方法来完成此操作,或者在我的事件中加入某种检查,以知道我是通过鼠标单击而不是制表符获得了键盘焦点?

5个回答

24

目前还没有看到任何干净的解决方案,可做的一件事情就是检查鼠标状态:

var tb = (TextBox)sender;
if (Mouse.LeftButton != MouseButtonState.Pressed)
    tb.SelectAll();

实际上有更好的方法,当焦点移动到键盘上时,您可以检查键盘。我建议使用正确的签名来处理GotKeyboardFocus事件以获取适当的事件参数:

private void TextBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    if (e.KeyboardDevice.IsKeyDown(Key.Tab))
        ((TextBox)sender).SelectAll();
}

此时,您可能仍然会看到一些选定内容在单击时被清除,但这只是因为前一个选择只有在失焦后才会隐藏。您可以在LostKeyboardFocus中清除选择以防止这种情况发生(例如:((TextBox)sender).Select(0, 0))。


@KDiTraglia:我希望e.Device也可以是一个MouseDevice,但是根据参数的名称,它只能是键盘,如果有一个if (e.Device is KeyboardDevice)的检查就好了。 - H.B.
但是如果他们在按住Tab键的同时用鼠标将文本框设置为焦点呢! - Kevin DiTraglia
@KDiTraglia:我怀疑那永远不会发生,如果你按住Tab键,焦点会一直循环。 - H.B.
我不确定您是怎么让KeyboardFocusChangedEventArgs工作的,对我而言,我使用的是RoutedEventArgs。在这种情况下,我使用了if (Keyboard.IsKeyDown(Key.Tab)),它起作用了。点赞! - Chris Ray
1
@ChrisRay:你可能使用了GotFocus,我的处理程序名称有误导性,我订阅了GotKeyboardFocus,它具有KeyboardFocusChangedEventArgs - H.B.

1
您可以尝试在焦点事件发生时检查鼠标是否存在于文本框中,并检查鼠标按钮状态。这并不完美,但应该接近您要寻找的内容。
private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
{
    TextBox txt = sender as TextBox;
    Point position = Mouse.GetPosition(txt);
    // if Mouse position is not in the TextBox Client Rectangle
    // and Mouse Button not Pressed.
    if((!(new Rect(0,0,txt.Width,txt.Height)).Contains(position)) || ( Mouse.LeftButton != MouseButtonState.Pressed))
        if (txt != null) txt.SelectAll();
}

正如H.B.所指出的那样,您可以尝试使用txt.IsMouseOver属性来确定光标是否在TextBox的客户端矩形内。这看起来更加简洁。
private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
{
    TextBox txt = sender as TextBox;
    if( !txt.IsMouseOver || Mouse.LeftButton != MouseButtonState.Pressed)
        if (txt != null) txt.SelectAll();
}

顺便提一下,有一个 UIElement.IsMouseOver 属性。 - H.B.
@H.B. 感谢提醒,我本来想有更简单的方法。 - Mark Hall

0

你可以在事件中捕获最后按下的键并进行比较

private Key lastKey;
protected override void OnKeyDown(KeyEventArgs e)
{
     lastKey = e.Key;
     base.OnKeyDown(e);
}

在你的事件中:

private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
{
    if(lastKey != Key.Tab)
        return;

    TextBox txt = sender as TextBox;
    if (txt != null) txt.SelectAll();
}

这并不完美,因为他们可能会按Tab键(而不是切换到您的控件),然后再单击您的控件。但大多数情况下它都能正常工作。


0

你可以使用附加行为模式

public class Behaviors
{
    public static readonly DependencyProperty SelectTextOnFocusProperty = DependencyProperty
        .RegisterAttached("SelectTextOnFocus", typeof(bool), typeof(Behaviors), new FrameworkPropertyMetadata(false, GotFocus));

    public static void SetSelectTextOnFocus(DependencyObject obj, bool value)
    {
        obj.SetValue(SelectTextOnFocusProperty, value);
    }

    private static void GotFocus(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textbox = d as TextBox;

        if (null == textbox) return;

        textbox.GotKeyboardFocus += SelectTextOnFocus;
        textbox.GotMouseCapture += SelectTextOnFocus;
    }

    private static void SelectTextOnFocus(object sender, RoutedEventArgs e)
    {
        if (!(sender is TextBox)) return;
        ((TextBox)sender).SelectAll();
    }
}

在你的 XAML 中只需要

xmlns:my="clr-namespace:Namespace;assembly=Rkmax"

你可以在文本框中使用它,例如:

<TextBox my:Behaviors.SelectTextOnFocus="True" />

所有这些工作都是为了鼠标和键盘事件


这会在使用鼠标选择时选择所有内容,但我不想要这样。 - Kevin DiTraglia
如果您不想使用鼠标选择文本,则可以删除行 textbox.GotMouseCapture += SelectTextOnFocus; - rkmax
是的,但最终情况是相同的(使用鼠标单击会导致gotkeyboardfocus触发)。 - Kevin DiTraglia
一个可能的解决方法是只在双击时执行{{SelectAll()}}。 - xmedeko

0

我为解决这个问题搜索了很多,找到了几个选择全部的解决方案。但是,问题在于当我们从文本框中选择一部分文本后右键剪切/复制时,它会选择全部文本,即使我只选择了一部分文本。为了解决这个问题,可以在键盘选择事件中添加以下代码。这对我有用。

    private static void SelectContentsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox)
        {
            TextBox textBox = d as TextBox;
            if ((e.NewValue as bool?).GetValueOrDefault(false))
            {
                textBox.GotKeyboardFocus += OnKeyboardFocusSelectText;                 
            }
            else
            {
                textBox.GotKeyboardFocus -= OnKeyboardFocusSelectText;

            }
        }
    }


    private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
    {
        if (e.KeyboardDevice.IsKeyDown(Key.Tab))
            ((TextBox)sender).SelectAll();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接