关于.net:使WinForms文本框的行为类似于浏览器的地址栏

关于.net:使WinForms文本框的行为类似于浏览器的地址栏

Making a WinForms TextBox behave like your browser's address bar

当C#WinForms文本框获得焦点时,我希望它的行为类似于浏览器的地址栏。

要了解我的意思,请在网络浏览器的地址栏中单击。 您会注意到以下行为:

  • 如果该文本框以前未聚焦,则单击该文本框应选择所有文本。
  • 向下滑动并在文本框中拖动,应仅选择用鼠标突出显示的文本。
  • 如果文本框已经聚焦,则单击不会选择所有文本。
  • 以编程方式或通过键盘制表键聚焦文本框应选择所有文本。
  • 我想在WinForms中完全做到这一点。

    最快速的提醒:请在回答之前阅读以下内容! 多谢你们。 :-)

    Calling .SelectAll() during
    the .Enter or .GotFocus events won't
    work because if the user clicked the
    textbox, the caret will be placed
    where he clicked, thus deselecting all
    text.

    Calling .SelectAll() during the .Click event won't work because the user won't be able to select any text with the mouse; the .SelectAll() call will keep overwriting the user's text selection.

    Calling BeginInvoke((Action)textbox.SelectAll) on focus/enter event enter doesn't work because it breaks rule #2 above, it will keep overriding the user's selection on focus.


    首先,感谢您的回答!共9个答案。谢谢。

    坏消息:所有答案都有一些古怪之处,或者说不是很正确(或根本没有)。我已在您的每个帖子中添加了评论。

    好消息:我已经找到一种使之起作用的方法。该解决方案非常简单,并且似乎可以在所有情况下使用(向下移动,选择文本,使用制表符焦点等)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    bool alreadyFocused;

    ...

    textBox1.GotFocus += textBox1_GotFocus;
    textBox1.MouseUp += textBox1_MouseUp;
    textBox1.Leave += textBox1_Leave;

    ...

    void textBox1_Leave(object sender, EventArgs e)
    {
        alreadyFocused = false;
    }


    void textBox1_GotFocus(object sender, EventArgs e)
    {
        // Select all text only if the mouse isn't down.
        // This makes tabbing to the textbox give focus.
        if (MouseButtons == MouseButtons.None)
        {
            this.textBox1.SelectAll();
            alreadyFocused = true;
        }
    }

    void textBox1_MouseUp(object sender, MouseEventArgs e)
    {
        // Web browsers like Google Chrome select the text on mouse up.
        // They only do it if the textbox isn't already focused,
        // and if the user hasn't selected all text.
        if (!alreadyFocused && this.textBox1.SelectionLength == 0)
        {
            alreadyFocused = true;
            this.textBox1.SelectAll();
        }
    }

    据我所知,这会使文本框的行为与Web浏览器的地址栏完全相同。

    希望这可以帮助下一个尝试解决这个看似简单的问题的人。

    再次感谢你们提供的所有答案,这些答案帮助我迈向正确的道路。


    我找到了一个更简单的解决方案。它涉及使用Control.BeginInvoke异步启动SelectAll,以便在发生Enter和Click事件之后发生:

    在C#中:

    1
    2
    3
    4
    5
    6
    7
    8
    private void MyTextBox_Enter(object sender, EventArgs e)
    {
        // Kick off SelectAll asyncronously so that it occurs after Click
        BeginInvoke((Action)delegate
        {
            MyTextBox.SelectAll();
        });
    }

    在VB.NET中(感谢Krishanu Dey)

    1
    2
    3
    Private Sub MyTextBox_Enter(sender As Object, e As EventArgs) Handles MyTextBox.Enter
        BeginInvoke(DirectCast(Sub() MyTextBox.SelectAll(), Action))
    End Sub

    您的解决方案很好,但是在一种特定情况下失败。如果通过选择文本范围而不是单击来使TextBox成为焦点,则notFocussed标志不会设置为true,因此当您第二次单击TextBox时,所有文本都会被选中。

    这是我的解决方案版本。我还将代码放入继承TextBox的类中,因此逻辑被很好地隐藏了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class MyTextBox : System.Windows.Forms.TextBox
    {
        private bool _focused;

        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            if (MouseButtons == MouseButtons.None)
            {
                SelectAll();
                _focused = true;
            }
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            _focused = false;
        }

        protected override void OnMouseUp(MouseEventArgs mevent)
        {
            base.OnMouseUp(mevent);
            if (!_focused)
            {
                if (SelectionLength == 0)
                    SelectAll();
                _focused = true;
            }
        }
    }

    这有点麻烦,但是在您的click事件中,请使用SendKeys.Send("{HOME}+{END}" );


    我使用的一行答案...您可能会踢自己...

    在Enter事件中:

    txtFilter.BeginInvoke(new MethodInvoker(txtFilter.SelectAll));


    文本框的点击事件?甚至MouseCaptureChanged事件都对我有用。 - 好。不起作用。

    因此,您必须做两件事:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    private bool f = false;

    private void textBox_MouseClick(object sender, MouseEventArgs e)
    {
      if (this.f) { this.textBox.SelectAll(); }
      this.f = false;
    }

    private void textBox_Enter(object sender, EventArgs e)
    {
      this.f = true;
      this.textBox.SelectAll();
    }
    private void textBox_MouseMove(object sender, MouseEventArgs e) // idea from the other answer
    {
      this.f = false;
    }

    也适用于制表符(通过textBoxes进行制表符)-以防万一,在Enter中调用SelectAll()。


    1
    2
    'Inside the Enter event
    TextBox1.SelectAll();

    好的,尝试之后,这里就是您想要的:

    • 在Enter事件上,启动一个标志,指出您已经进入enter事件
    • 在Click事件上,如果设置了标志,则调用.SelectAll()并重置标志。
    • 在MouseMove事件上,将输入的标志设置为false,这将允许您单击突出显示,而不必先输入文本框。

    这选择了所有输入的文本,但是允许我随后突出显示部分文本,或者允许您在第一次单击时突出显示。

    按要求:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
        bool entered = false;
        private void textBox1_Enter(object sender, EventArgs e)
        {
            entered = true;
            textBox1.SelectAll();   //From Jakub's answer.
        }

        private void textBox1_Click(object sender, EventArgs e)
        {
            if (entered) textBox1.SelectAll();
            entered = false;
        }

        private void textBox1_MouseMove(object sender, MouseEventArgs e)
        {
            if (entered) entered = false;
        }

    对我来说,跳到控件中会选择所有文本。


    这是一个帮助程序功能,它将解决方案提高到了一个新级别-无需继承即可重用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
        public static void WireSelectAllOnFocus( TextBox aTextBox )
        {
            bool lActive = false;
            aTextBox.GotFocus += new EventHandler( ( sender, e ) =>
            {
                if ( System.Windows.Forms.Control.MouseButtons == MouseButtons.None )
                {
                    aTextBox.SelectAll();
                    lActive = true;
                }
            } );

            aTextBox.Leave += new EventHandler( (sender, e ) => {
                lActive = false;
            } );

            aTextBox.MouseUp += new MouseEventHandler( (sender, e ) => {
                if ( !lActive )
                {
                    lActive = true;
                    if ( aTextBox.SelectionLength == 0 ) aTextBox.SelectAll();
                }  
            });
        }

    要使用此功能,只需调用传递TextBox的函数即可,它会为您处理所有混乱的位。我建议在Form_Load事件中连接所有文本框。您可以将此函数放在您的表单中,或者如果您喜欢我,可以将其放在实用程序类中的某个位置,以实现更多重用。


    这类似于nzhenry的流行答案,但是我发现不必继承子类更容易:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    Private LastFocused As Control = Nothing

    Private Sub TextBox1_Enter(sender As Object, e As System.EventArgs) Handles TextBox1.Enter, TextBox2.Enter, TextBox3.Enter
        If MouseButtons = Windows.Forms.MouseButtons.None Then LastFocused = sender
    End Sub

    Private Sub TextBox1_Leave(sender As Object, e As System.EventArgs) Handles TextBox1.Leave, TextBox2.Leave, TextBox3.Leave
        LastFocused = Nothing
    End Sub

    Private Sub TextBox1_MouseUp(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles TextBox1.MouseUp, TextBox2.MouseUp, TextBox3.MouseUp
        With CType(sender, TextBox)
            If LastFocused IsNot sender AndAlso .SelectionLength = 0 Then .SelectAll()
        End With
        LastFocused = sender
    End Sub


    这适用于WPF / XAML文本框。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        private bool initialEntry = true;
        private void TextBox_SelectionChanged(object sender, RoutedEventArgs e)
        {
            if (initialEntry)
            {
                e.Handled = true;
                initialEntry = false;
                TextBox.SelectAll();
            }
        }
        private void TextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            TextBox.SelectAll();
            initialEntry = true;      
        }

    SelectAll从来没有为我工作。

    这可行。

    1
    2
    3
    ActiveControl = textBox1;
    textBox1->SelectionStart = 0;
    textBox1->SelectionLength = textBox1->Text->Length;

    我找到了一个更简单的解决方案:

    若要确保在单击文本框时选择了所有文本,请确保Click处理程序调用Enter处理程序。不需要额外的变量!

    例:

    1
    2
    3
    4
    5
    6
    7
    8
    private void textBox1_Click(object sender, EventArgs e){
            textBox1_Enter(sender, e);
        }

    private void textBox1_Enter(object sender, EventArgs e){
            TextBox tb = ((TextBox)sender);
            tb.SelectAll();
        }

    有趣的是,我认为,具有DropDownStyle = Simple的ComboBox几乎具有您正在寻找的行为。

    (如果减小控件的高度以不显示列表,然后再增加几个像素,则ComboBox和TextBox之间没有有效的区别。)


    我在MouseUp事件中调用了SelectAll,对我来说效果很好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        private bool _tailTextBoxFirstClick = false;

        private void textBox1_MouseUp(object sender, MouseEventArgs e)
        {
            if(_textBoxFirstClick)          
                textBox1.SelectAll();

            _textBoxFirstClick = false;
        }  

        private void textBox1_Leave(object sender, EventArgs e)
        {
            _textBoxFirstClick = true;
            textBox1.Select(0, 0);
        }


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    private bool _isSelected = false;
    private void textBox_Validated(object sender, EventArgs e)
    {
        _isSelected = false;
    }

    private void textBox_MouseClick(object sender, MouseEventArgs e)
    {
        SelectAllText(textBox);
    }

    private void textBox_Enter(object sender, EventArgs e)
    {
        SelectAllText(textBox);
    }

    private void SelectAllText(TextBox text)
    {
        if (!_isSelected)
        {
            _isSelected = true;
            textBox.SelectAll();
        }
    }

    我的解决方案很原始,但可以很好地达到我的目的

    1
    2
    3
    4
    5
    6
    7
    8
    private async void TextBox_GotFocus(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox)
        {
            await Task.Delay(100);
            (sender as TextBox).SelectAll();
        }
    }

    为什么不简单地使用文本框的MouseDown-Event?它对我来说很好用,不需要额外的布尔值。非常干净和简单,例如:

    1
    2
    3
    4
    5
    private void textbox_MouseDown(object sender, MouseEventArgs e) {
        if (textbox != null && !string.IsNullOrEmpty(textbox.Text))
        {
            textbox.SelectAll();
        } }

    您是否尝试过在MSDN论坛" Windows Forms General"上建议的仅将TextBox子类化的解决方案?


    实际上,GotFocus是您感兴趣的正确事件(实际上是消息),因为无论您如何控制控件,最终都可以得到。问题是何时调用SelectAll()。

    尝试这个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.textBox1.GotFocus += new EventHandler(textBox1_GotFocus);
        }

        private delegate void SelectAllDelegate();    
        private IAsyncResult _selectAllar = null; //So we can clean up afterwards.

        //Catch the input focus event
        void textBox1_GotFocus(object sender, EventArgs e)
        {
            //We could have gotten here many ways (including mouse click)
            //so there could be other messages queued up already that might change the selection.
            //Don't call SelectAll here, since it might get undone by things such as positioning the cursor.
            //Instead use BeginInvoke on the form to queue up a message
            //to select all the text after everything caused by the current event is processed.
            this._selectAllar = this.BeginInvoke(new SelectAllDelegate(this._SelectAll));
        }

        private void _SelectAll()
        {
            //Clean-up the BeginInvoke
            if (this._selectAllar != null)
            {
                this.EndInvoke(this._selectAllar);
            }
            //Now select everything.
            this.textBox1.SelectAll();
        }
    }


    对于表单中的一组文本框:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    private System.Windows.Forms.TextBox lastFocus;  

    private void textBox_GotFocus(object sender, System.Windows.Forms.MouseEventArgs e)  
    {
        TextBox senderTextBox = sender as TextBox;
        if (lastFocus!=senderTextBox){
            senderTextBox.SelectAll();
        }
        lastFocus = senderTextBox;  
    }

    只需从TextBox或MaskedTextBox派生一个类:

    1
    2
    3
    4
    5
    6
    7
    8
    public class SMaskedTextBox : MaskedTextBox
    {
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            this.SelectAll();
        }
    }

    并在您的表单上使用它。


    当鼠标单击而不是立即释放时,我发现此方法最有效:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
        private bool SearchBoxInFocusAlready = false;
        private void SearchBox_LostFocus(object sender, RoutedEventArgs e)
        {
            SearchBoxInFocusAlready = false;
        }

        private void SearchBox_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (e.ButtonState == MouseButtonState.Released && e.ChangedButton == MouseButton.Left &&
                SearchBox.SelectionLength == 0 && SearchBoxInFocusAlready == false)
            {
                SearchBox.SelectAll();
            }

            SearchBoxInFocusAlready = true;
        }

    我知道这已经解决了,但我建议我认为实际上很简单。

    在鼠标上移事件中,您要做的就是放置

    1
    2
    3
    4
    if(textBox.SelectionLength = 0)
    {
        textBox.SelectAll();
    }

    它似乎在VB.NET中对我有用(我知道这是一个C#问题...可悲的是我被迫在工作中使用VB ..而我遇到了这个问题,这就是让我来到这里的原因... )

    我还没有发现任何问题..除了它没有立即单击时选择,但我对此有问题...。


    以下解决方案适用于我。我添加了OnKeyDownOnKeyUp事件重写以使TextBox文本始终处于选中状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
        public class NumericTextBox : TextBox
    {
        private bool _focused;
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            if (MouseButtons == MouseButtons.None)
            {
                this.SelectAll();
                _focused = true;
            }
        }
        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);
            if (MouseButtons == MouseButtons.None)
            {
                SelectAll();
                _focused = true;
            }
        }

        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            _focused = false;
        }

        protected override void OnMouseUp(MouseEventArgs mevent)
        {
            base.OnMouseUp(mevent);
            if (!_focused)
            {
                if (SelectionLength == 0)
                    SelectAll();
                _focused = true;
            }
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            if (SelectionLength == 0)
                SelectAll();
            _focused = true;
        }
        protected override void OnKeyDown(KeyEventArgs e)
        {
           base.OnKeyDown(e);
           if (SelectionLength == 0)
                SelectAll();
            _focused = true;
        }
    }


    离开控件时设置选择。当您回来时它将在那里。在表单中使用Tab键查看,然后返回到控件时,将选中所有文本。

    如果您使用鼠标进入,则插入标记将正确地放置在您单击的位置。

    1
    2
    3
    4
    private void maskedTextBox1_Leave(object sender, CancelEventArgs e)
        {
            maskedTextBox1.SelectAll();
        }

    很简单的解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
        private bool _focusing = false;

        protected override void OnEnter( EventArgs e )
        {
            _focusing = true;
            base.OnEnter( e );
        }

        protected override void OnMouseUp( MouseEventArgs mevent )
        {
            base.OnMouseUp( mevent );

            if( _focusing )
            {
                this.SelectAll();
                _focusing = false;
            }
        }

    编辑:原始OP特别关注鼠标向下/文本选择/鼠标向上的顺序,在这种情况下,上述简单的解决方案最终会导致文本被部分选中。

    这应该可以解决*问题(实际上,我拦截了WM_SETCURSOR):

    1
    2
    3
    4
    5
    6
    7
    8
    9
        protected override void WndProc( ref Message m )
        {
            if( m.Msg == 32 ) //WM_SETCURSOR=0x20
            {
                  this.SelectAll(); // or your custom logic here                
            }

            base.WndProc( ref m );
        }

    *实际上,以下序列以部分文本选择结束,但是如果将鼠标移到文本框中,则会再次选择所有文本:

    鼠标向下/文本选择/鼠标移出文本框/鼠标向上


    只需在enter和click事件上使用selectall()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    private void textBox1_Enter(object sender, EventArgs e)
            {

                textBox1.SelectAll();
            }
            private void textBox1_Click(object sender, EventArgs e)
            {
                textBox1.SelectAll();
            }

    在.NET 2005中,这对我有用-

    1
    2
    3
    4
    5
    6
        ' * if the mouse button is down, do not run the select all.
        If MouseButtons = Windows.Forms.MouseButtons.Left Then
            Exit Sub
        End If

     ' * OTHERWISE INVOKE THE SELECT ALL AS DISCUSSED.

    以下似乎有效。
    enter事件处理到控件的选项卡,单击控件时MouseDown起作用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
        private ########### void textBox1_Enter(object sender, EventArgs e)
        {
            textBox1.SelectAll();
        }

        private void textBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (textBox1.Focused)
                textBox1.SelectAll();
        }

    答案实际上比上面的所有答案都简单得多,例如(在WPF中):

    1
    2
    3
    4
    5
    public void YourTextBox_MouseEnter(object sender, MouseEventArgs e)
        {
            YourTextBox.Focus();
            YourTextBox.SelectAll();
        }

    当然,我不知道您想如何使用此代码,但是这里要看的主要部分是:首先调用.Focus(),然后调用.SelectAll();。


    我创建了一个新的VB.Net Wpf项目。我创建了一个文本框,并将以下内容用于代码隐藏:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    Class MainWindow

        Sub New()

            ' This call is required by the designer.
            InitializeComponent()

            ' Add any initialization after the InitializeComponent() call.
            AddHandler PreviewMouseLeftButtonDown, New MouseButtonEventHandler(AddressOf SelectivelyIgnoreMouseButton)
            AddHandler GotKeyboardFocus, New KeyboardFocusChangedEventHandler(AddressOf SelectAllText)
            AddHandler MouseDoubleClick, New MouseButtonEventHandler(AddressOf SelectAllText)
        End Sub

        Private Shared Sub SelectivelyIgnoreMouseButton(ByVal sender As Object, ByVal e As MouseButtonEventArgs)
            ' Find the TextBox
            Dim parent As DependencyObject = TryCast(e.OriginalSource, UIElement)
            While parent IsNot Nothing AndAlso Not (TypeOf parent Is TextBox)
                parent = VisualTreeHelper.GetParent(parent)
            End While

            If parent IsNot Nothing Then
                Dim textBox As Object = DirectCast(parent, TextBox)
                If Not textBox.IsKeyboardFocusWithin Then
                    ' 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
                End If
            End If
        End Sub

        Private Shared Sub SelectAllText(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim textBox As Object = TryCast(e.OriginalSource, TextBox)
            If textBox IsNot Nothing Then
                textBox.SelectAll()
            End If
        End Sub

    End Class

    推荐阅读