使WinForms TextBox的行为类似于浏览器的地址栏
当CWinForms文本框获得焦点时,我希望它的行为类似于浏览器的地址栏。
要了解我的意思,请在网络浏览器的地址栏中单击。我们会注意到以下行为:
- 如果文本框以前未聚焦,则单击文本框应选择所有文本。
- 向下滑动并在文本框中拖动,应仅选择用鼠标突出显示的文本。
- 如果文本框已经聚焦,则单击不会选择所有文本。
- 以编程方式或者通过键盘选项卡聚焦文本框应选择所有文本。
我想在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.
解决方案
文本框的点击事件?甚至MouseCaptureChanged事件都对我有用。好的。不起作用。
因此,我们必须做两件事:
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; }
用于制表符(通过文本框到其中一个)以及在Enter中调用SelectAll()以防万一...
这有点麻烦,但是在点击事件中,请使用SendKeys.Send(" {HOME} + {END}");
。
'Inside the Enter event TextBox1.SelectAll();
好的,尝试之后,这里就是我们想要的:
- 在Enter事件上,启动一个标志,指出我们曾经在enter事件中
- 在Click事件上,如果设置了标志,则调用.SelectAll()并重置标志。
- 在MouseMove事件上,将输入的标志设置为false,这将允许我们单击突出显示,而不必先输入文本框。
这选择了所有输入的文本,但是允许我随后突出显示部分文本,或者允许我们在第一次单击时突出显示。
按要求:
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; }
对我来说,跳到控件中会选择所有文本。
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(); } }
以下似乎有效。
enter事件处理到控件的选项卡,单击控件时MouseDown起作用。
private ########### void textBox1_Enter(object sender, EventArgs e) { textBox1.SelectAll(); } private void textBox1_MouseDown(object sender, MouseEventArgs e) { if (textBox1.Focused) textBox1.SelectAll(); }
有趣的是,我认为,具有DropDownStyle = Simple的ComboBox几乎具有我们正在寻找的行为。
(如果减小控件的高度以不显示列表,然后再减小几个像素,则ComboBox和TextBox之间没有有效的区别。)
实际上,GotFocus是我们感兴趣的正确事件(实际上是消息),因为无论我们如何控制控件,最终都将得到它。问题是何时调用SelectAll()。
试试这个:
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(); } }
首先,感谢回答!共9个答案。谢谢你。
坏消息:所有答案都有一些古怪之处,或者说不是很正确(或者根本没有)。我已在每个帖子中添加了评论。
好消息:我已经找到一种使之起作用的方法。该解决方案非常简单,并且似乎可以在所有情况下使用(向下移动,选择文本,使用制表符焦点等)。
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浏览器的地址栏完全相同。
希望这可以帮助下一个尝试解决这个看似简单的问题的人。
再次感谢你们提供的所有答案,这些答案帮助我迈向正确的道路。
我们为什么不简单地使用文本框的MouseDown-Event?它对我来说很好用,不需要额外的布尔值。非常干净和简单,例如:
private void textbox_MouseDown(object sender, MouseEventArgs e) { if (textbox != null && !string.IsNullOrEmpty(textbox.Text)) { textbox.SelectAll(); } }
我们是否尝试过在MSDN论坛" Windows Forms General"上建议的仅将TextBox子类化的解决方案?
我使用的一行答案...我们可能会踢自己...
在Enter事件中:
txtFilter.BeginInvoke(new MethodInvoker(txtFilter.SelectAll));
我在MouseUp事件中调用了SelectAll,对我来说效果很好。
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); }
只需从TextBox或者MaskedTextBox派生一个类:
public class SMaskedTextBox : MaskedTextBox { protected override void OnGotFocus(EventArgs e) { base.OnGotFocus(e); this.SelectAll(); } }
并在表单上使用它。
我创建了一个新的VB.Net Wpf项目。我创建了一个文本框,并将以下内容用于代码隐藏:
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
解决方案很好,但是在一种特定情况下失败。如果通过选择文本范围而不是单击来使TextBox成为焦点,则notFocussed标志不会设置为true,因此当我们第二次单击TextBox时,所有文本都会被选中。
这是我的解决方案版本。我还将代码放入继承TextBox的类中,因此逻辑被很好地隐藏了。
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; } } }