Как автоматически выделить весь текст в фокусе в WPF TextBox? - PullRequest
204 голосов
/ 19 марта 2009

Если я вызываю SelectAll из обработчика событий GotFocus, он не работает с мышью - выбор исчезает, как только мышь отпускается.

РЕДАКТИРОВАТЬ: Людям нравится ответ Доннелле, я постараюсь объяснить, почему он мне не понравился так же, как принятый ответ.

  • Это более сложно, в то время как принятый ответ делает то же самое проще.
  • Юзабилити принятого ответа лучше. Когда вы щелкаете в середине текста, текст становится невыбранным, когда вы отпускаете мышь, позволяя мгновенно начать редактирование, и если вы все еще хотите выбрать все, просто нажмите кнопку еще раз, и на этот раз он не будет отменен при отпускании. Следуя рецепту Донеллы, если я нажму на середину текста, мне придется щелкнуть второй раз, чтобы иметь возможность редактировать. Если я щелкну где-нибудь внутри текста, а не за его пределами, это, скорее всего, означает, что я хочу начать редактирование, а не перезаписывать все.

Ответы [ 28 ]

198 голосов
/ 19 марта 2009

У нас это есть, поэтому первый щелчок выбирает все, а другой щелчок переходит к курсору (наше приложение предназначено для использования на планшетах с ручками).

Возможно, вы найдете это полезным.

public class ClickSelectTextBox : TextBox
{
    public ClickSelectTextBox()
    {
        AddHandler(PreviewMouseLeftButtonDownEvent, 
          new MouseButtonEventHandler(SelectivelyIgnoreMouseButton), true);
        AddHandler(GotKeyboardFocusEvent, 
          new RoutedEventHandler(SelectAllText), true);
        AddHandler(MouseDoubleClickEvent, 
          new RoutedEventHandler(SelectAllText), true);
    }

    private static void SelectivelyIgnoreMouseButton(object sender, 
                                                     MouseButtonEventArgs e)
    {
        // Find the TextBox
        DependencyObject parent = e.OriginalSource as UIElement;
        while (parent != null && !(parent is TextBox))
            parent = VisualTreeHelper.GetParent(parent);

        if (parent != null)
        {
            var textBox = (TextBox)parent;
            if (!textBox.IsKeyboardFocusWithin)
            {
                // If the text box is not yet focussed, give it the focus and
                // stop further processing of this click event.
                textBox.Focus();
                e.Handled = true;
            }
        }
    }

    private static void SelectAllText(object sender, RoutedEventArgs e)
    {
        var textBox = e.OriginalSource as TextBox;
        if (textBox != null)
            textBox.SelectAll();
    }
}
150 голосов
/ 31 марта 2010

Ответ Доннелле работает лучше всего, но необходимость выучить новый класс для его использования - это боль.

Вместо этого я регистрирую обработчики в App.xaml.cs для всех TextBoxes в приложении. Это позволяет мне использовать ответ Доннелле со стандартным элементом управления TextBox.

Добавьте следующие методы в ваш App.xaml.cs:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e) 
    {
        // Select the text in a TextBox when it receives focus.
        EventManager.RegisterClassHandler(typeof(TextBox), TextBox.PreviewMouseLeftButtonDownEvent,
            new MouseButtonEventHandler(SelectivelyIgnoreMouseButton));
        EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotKeyboardFocusEvent, 
            new RoutedEventHandler(SelectAllText));
        EventManager.RegisterClassHandler(typeof(TextBox), TextBox.MouseDoubleClickEvent,
            new RoutedEventHandler(SelectAllText));
        base.OnStartup(e); 
    }

    void SelectivelyIgnoreMouseButton(object sender, MouseButtonEventArgs e)
    {
        // Find the TextBox
        DependencyObject parent = e.OriginalSource as UIElement;
        while (parent != null && !(parent is TextBox))
            parent = VisualTreeHelper.GetParent(parent);

        if (parent != null)
        {
            var textBox = (TextBox)parent;
            if (!textBox.IsKeyboardFocusWithin)
            {
                // If the text box is not yet focused, give it the focus and
                // stop further processing of this click event.
                textBox.Focus();
                e.Handled = true;
            }
        }
    }

    void SelectAllText(object sender, RoutedEventArgs e)
    {
        var textBox = e.OriginalSource as TextBox;
        if (textBox != null)
            textBox.SelectAll();
    }
}
71 голосов
/ 20 апреля 2010

Это довольно старый, но я все равно покажу свой ответ.
Я выбрал часть ответа Доннелла (пропустил двойной щелчок), так как думаю, что это вызывает наименьшее удивление у пользователей. Однако, как и gcores, мне не нравится необходимость создания производного класса. Но мне также не нравится метод gcores «при запуске ...». И мне это нужно на основе "как правило, но не всегда".

Я реализовал это как свойство присоединенной зависимости, поэтому я могу установить SelectTextOnFocus.Active=True в xaml. Я считаю этот путь самым приятным.

namespace foo.styles.behaviour
{
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Media;

    public class SelectTextOnFocus : DependencyObject
    {
        public static readonly DependencyProperty ActiveProperty = DependencyProperty.RegisterAttached(
            "Active",
            typeof(bool),
            typeof(SelectTextOnFocus),
            new PropertyMetadata(false, ActivePropertyChanged));

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

        private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DependencyObject dependencyObject = GetParentFromVisualTree(e.OriginalSource);

            if (dependencyObject == null)
            {
                return;
            }

            var textBox = (TextBox)dependencyObject;
            if (!textBox.IsKeyboardFocusWithin)
            {
                textBox.Focus();
                e.Handled = true;
            }
        }

        private static DependencyObject GetParentFromVisualTree(object source)
        {
            DependencyObject parent = source as UIElement;
            while (parent != null && !(parent is TextBox))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }

            return parent;
        }

        private static void OnKeyboardFocusSelectText(object sender, KeyboardFocusChangedEventArgs e)
        {
            TextBox textBox = e.OriginalSource as TextBox;
            if (textBox != null)
            {
                textBox.SelectAll();
            }
        }

        [AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
        [AttachedPropertyBrowsableForType(typeof(TextBox))]
        public static bool GetActive(DependencyObject @object)
        {
            return (bool) @object.GetValue(ActiveProperty);
        }

        public static void SetActive(DependencyObject @object, bool value)
        {
            @object.SetValue(ActiveProperty, value);
        }
    }
}

Для моей "общей, но не всегда" -функции я установил для этого свойства значение True в (глобальном) стиле TextBox. Таким образом, «выбор текста» всегда «включен», но я могу отключить его для каждого текстового поля.

70 голосов
/ 19 марта 2009

Не знаю, почему он теряет выделение в событии GotFocus.

Но одним из решений является выбор в событиях GotKeyboardFocus и GotMouseCapture. Таким образом, это всегда будет работать.

40 голосов
/ 12 августа 2009

Вот варианты поведения Blend, реализующие решение для ответа:

Один для присоединения к одному TextBox:

public class SelectAllTextOnFocusBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.GotKeyboardFocus += AssociatedObjectGotKeyboardFocus;
        AssociatedObject.GotMouseCapture += AssociatedObjectGotMouseCapture;
        AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObjectPreviewMouseLeftButtonDown;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.GotKeyboardFocus -= AssociatedObjectGotKeyboardFocus;
        AssociatedObject.GotMouseCapture -= AssociatedObjectGotMouseCapture;
        AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObjectPreviewMouseLeftButtonDown;
    }

    private void AssociatedObjectGotKeyboardFocus(object sender,
        System.Windows.Input.KeyboardFocusChangedEventArgs e)
    {
        AssociatedObject.SelectAll();
    }

    private void AssociatedObjectGotMouseCapture(object sender,
        System.Windows.Input.MouseEventArgs e)
    {
        AssociatedObject.SelectAll();   
    }

    private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if(!AssociatedObject.IsKeyboardFocusWithin)
        {
            AssociatedObject.Focus();
            e.Handled = true;
        }
    }
}

И один для присоединения к корню контейнера, содержащего несколько TextBox'ов:

public class SelectAllTextOnFocusMultiBehavior : Behavior<UIElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.GotKeyboardFocus += HandleKeyboardFocus;
        AssociatedObject.GotMouseCapture += HandleMouseCapture;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.GotKeyboardFocus -= HandleKeyboardFocus;
        AssociatedObject.GotMouseCapture -= HandleMouseCapture;
    }

    private static void HandleKeyboardFocus(object sender,
        System.Windows.Input.KeyboardFocusChangedEventArgs e)
    {
        var txt = e.NewFocus as TextBox;
        if (txt != null)
            txt.SelectAll();
    }

    private static void HandleMouseCapture(object sender,
        System.Windows.Input.MouseEventArgs e)
    {
        var txt = e.OriginalSource as TextBox;
        if (txt != null)
            txt.SelectAll();
    }
}
14 голосов
/ 02 мая 2013

Вот очень хорошее очень простое решение для MSDN :

<TextBox
    MouseDoubleClick="SelectAddress"
    GotKeyboardFocus="SelectAddress"
    PreviewMouseLeftButtonDown="SelectivelyIgnoreMouseButton" />

Вот код:

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

private void SelectivelyIgnoreMouseButton(object sender,
    MouseButtonEventArgs e)
{
    TextBox tb = (sender as TextBox);
    if (tb != null)
    {
        if (!tb.IsKeyboardFocusWithin)
        {
            e.Handled = true;
            tb.Focus();
        }
    }
}
13 голосов
/ 25 июля 2013

Хотя это старый вопрос, я только что столкнулся с этой проблемой, но решил ее, используя присоединенное поведение, а не поведение выражения, как в ответе Сергея. Это означает, что мне не нужна зависимость от System.Windows.Interactivity в Blend SDK:

public class TextBoxBehavior
{
    public static bool GetSelectAllTextOnFocus(TextBox textBox)
    {
        return (bool)textBox.GetValue(SelectAllTextOnFocusProperty);
    }

    public static void SetSelectAllTextOnFocus(TextBox textBox, bool value)
    {
        textBox.SetValue(SelectAllTextOnFocusProperty, value);
    }

    public static readonly DependencyProperty SelectAllTextOnFocusProperty =
        DependencyProperty.RegisterAttached(
            "SelectAllTextOnFocus",
            typeof (bool),
            typeof (TextBoxBehavior),
            new UIPropertyMetadata(false, OnSelectAllTextOnFocusChanged));

    private static void OnSelectAllTextOnFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBox = d as TextBox;
        if (textBox == null) return;

        if (e.NewValue is bool == false) return;

        if ((bool) e.NewValue)
        {
            textBox.GotFocus += SelectAll;
            textBox.PreviewMouseDown += IgnoreMouseButton;
        }
        else
        {
            textBox.GotFocus -= SelectAll;
            textBox.PreviewMouseDown -= IgnoreMouseButton;
        }
    }

    private static void SelectAll(object sender, RoutedEventArgs e)
    {
        var textBox = e.OriginalSource as TextBox;
        if (textBox == null) return;
        textBox.SelectAll();
    }

    private static void IgnoreMouseButton(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        var textBox = sender as TextBox;
        if (textBox == null || textBox.IsKeyboardFocusWithin) return;

        e.Handled = true;
        textBox.Focus();
    }
}

Затем вы можете использовать его в своем XAML следующим образом:

<TextBox Text="Some Text" behaviors:TextBoxBehavior.SelectAllTextOnFocus="True"/>

Я писал об этом здесь .

10 голосов
/ 24 февраля 2012

Я думаю, что это хорошо работает:

private void ValueText_GotFocus(object sender, RoutedEventArgs e)
    {
        TextBox tb = (TextBox)e.OriginalSource;
        tb.Dispatcher.BeginInvoke(
            new Action(delegate
                {
                    tb.SelectAll();
                }), System.Windows.Threading.DispatcherPriority.Input);
    }

Если вы хотите реализовать его как метод расширения:

public static void SelectAllText(this System.Windows.Controls.TextBox tb)
    {
        tb.Dispatcher.BeginInvoke(
            new Action(delegate
            {
                tb.SelectAll();
            }), System.Windows.Threading.DispatcherPriority.Input);
    }

А в вашем событии GotFocus:

private void ValueText_GotFocus(object sender, RoutedEventArgs e)
    {
        TextBox tb = (TextBox)e.OriginalSource;
        tb.SelectAllText();
    }

Я обнаружил решение выше, потому что несколько месяцев назад я искал способ установить фокус на конкретный элемент UIElement. Я где-то обнаружил код ниже (кредит предоставляется), и он работает хорошо. Я публикую его, хотя он не имеет прямого отношения к вопросу OP, поскольку демонстрирует ту же схему использования Dispatcher для работы с UIElement.

// Sets focus to uiElement
    public static void DelayedFocus(this UIElement uiElement)
    {
        uiElement.Dispatcher.BeginInvoke(
        new Action(delegate
        {
            uiElement.Focusable = true;
            uiElement.Focus();
            Keyboard.Focus(uiElement);
        }),
        DispatcherPriority.Render);
    }
5 голосов
/ 27 июля 2010

Я не нашел ни одного из представленных здесь ответов, имитирующих стандартное текстовое поле Windows. Например, попробуйте щелкнуть пробел между последним символом текстового поля и правой стороной текстового поля. Большинство решений здесь всегда будут выбирать весь контент, что затрудняет добавление текста в текстовое поле.

Ответ, который я здесь представляю, ведет себя лучше в этом отношении. Это поведение (поэтому требуется сборка System.Windows.Interactivity из Blend SDK ). Его также можно переписать, используя вложенные свойства.

public sealed class SelectAllTextOnFocusBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObject_PreviewMouseLeftButtonDown;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObject_PreviewMouseLeftButtonDown;
    }

    void AssociatedObject_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        // Find the textbox
        DependencyObject parent = e.OriginalSource as UIElement;
        while (parent != null && !(parent is TextBox))
            parent = VisualTreeHelper.GetParent(parent);

        var textBox = parent as TextBox;
        Debug.Assert(textBox != null);

        if (textBox.IsFocused) return;

        textBox.SelectAll();
        Keyboard.Focus(textBox);
        e.Handled = true;
    }
}

Это основано на коде, который я нашел здесь .

4 голосов
/ 21 октября 2013

Эта простая реализация отлично работает для меня:

void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
    ((TextBox) sender).SelectAll();
}

void TextBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    var TextBox = (TextBox) sender;
    if (!TextBox.IsKeyboardFocusWithin)
    {
        TextBox.Focus();
        e.Handled = true;
    }
}

Чтобы применить его ко всем TextBox, поставьте следующий код после InitializeComponent();

EventManager.RegisterClassHandler(typeof(TextBox), TextBox.GotFocusEvent, new RoutedEventHandler(TextBox_GotFocus));
EventManager.RegisterClassHandler(typeof(TextBox), TextBox.PreviewMouseDownEvent, new MouseButtonEventHandler(TextBox_PreviewMouseDown));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...