Textbox SelectAll on tab but not mouse click

2019-02-16 08:01发布

问题:

So lets say I have a WPF form with several text boxes, if you tab to the text box and it already has something in it, I want to select all the text in that box so typing will erase that text. If you mouse click on the box, it probably means you want to change a letter somewhere, so do not highlight all in this case. Seems easy enough, but a good solution as so far eluded me. Here's what I have so far that is very close to working, but not quite perfect.

<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();
}

So when the box gets keyboard focus it selects all, so tabbing to the text box selects all the text perfectly. However if the mouse clicks this method gets called as well, which also highlights the text, but the click then puts the cursor where the mouse clicked after. So functionally it's perfect, but it still bothers me that it flickers selecting everything when the mouse clicks. Any better way to do this, or put some kind of check in my event to know that I gained keyboard focus from a mouse click and not a tab?

回答1:

Have not seen any clean solution so far sadly, one thing you could do is just check the mouse state:

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

But there actually is a better way, as the focus shifts on key down you can check the keyboard instead. I would recommend using the proper signature for the GotKeyboardFocus handler to get the appropriate event-args:

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

At this point you may still see some selection getting cleared upon click but that is just because the previous selection only gets hidden if unfocused. You can always clear the selection in LostKeyboardFocus to prevent that (e.g. ((TextBox)sender).Select(0, 0)).



回答2:

You could try checking if the Mouse is present in the TextBox when the Focus Event happens and check the Mouse ButtonButtonState. This is not perfect but should be close to what you are looking for.

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();
}

and as H.B. Pointed out you could try using the txt.IsMouseOver Property to determine if the Cursor is inside the TextBox's Client Rectangle. It looks a lot cleaner.

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();
}


回答3:

You could capture the last key pressed and compare against it in your event

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

and in your event:

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

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

It's not perfect because they could have hit tab (not tabbing into your control) and than click into it your control. But it will work most of the time.



回答4:

You can use attached behavior pattern

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();
    }
}

in your xaml only need

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

use you can use in a TextBox like

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

all this work for mouse and keyboard event



回答5:

I searched a lot for the solution, I found couple of solutions to selectall But, the issue is when we do right click and do cut/copy after selecting part of text from text box, it selects all even I selected part of text. To fix this here is the solution. Just add the below code in the keyboard select event. This worked for me.

    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();
    }