使 WinForms TextBox 的行为类似于浏览器的地址栏
-
01-07-2019 - |
题
当 C# WinForms 文本框获得焦点时,我希望它的行为就像浏览器的地址栏一样。
要明白我的意思,请单击网络浏览器的地址栏。您会注意到以下行为:
- 如果文本框之前未获得焦点,则单击文本框应选择所有文本。
- 按下鼠标并在文本框中拖动应仅选择我用鼠标突出显示的文本。
- 如果文本框已获得焦点,则单击不会选择所有文本。
- 以编程方式或通过键盘选项卡聚焦文本框应选择所有文本。
我想在 WinForms 中做到这一点。
最快的枪支警报:请在回答前阅读以下内容! 多谢你们。:-)
在.enter或.gotFocus事件期间呼叫.selectall()无法使用 因为如果用户单击了文本框,则将其放置在他单击的位置,从而取消选择所有文本。
在 .Click 事件期间调用 .SelectAll() 不起作用 因为用户无法用鼠标选择任何文本;.SelectAll() 调用将继续覆盖用户的文本选择。
在焦点/输入事件输入上调用 BeginInvoke((Action)textbox.SelectAll) 不起作用 因为它违反了上面的规则#2,所以它将继续覆盖用户对焦点的选择。
解决方案
首先感谢各位的解答!总共 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();
}
}
据我所知,这会导致文本框的行为与网络浏览器的地址栏完全相同。
希望这可以帮助下一个尝试解决这个看似简单的问题的人。
再次感谢你们,你们的所有答案帮助我走上了正确的道路。
其他提示
我找到了一个更简单的解决方案。它涉及使用异步方式启动 SelectAll Control.BeginInvoke
以便在 Enter 和 Click 事件发生后发生:
在 C# 中:
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 中(感谢 克里沙努·戴伊)
Private Sub MyTextBox_Enter(sender As Object, e As EventArgs) Handles MyTextBox.Enter
BeginInvoke(DirectCast(Sub() MyTextBox.SelectAll(), Action))
End Sub
您的解决方案很好,但在一种特定情况下失败了。如果通过选择文本范围而不是仅单击来为 TextBox 提供焦点,则alreadyFocussed 标志不会设置为 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;
}
}
}
这有点混乱,但是在您的点击事件中,使用 SendKeys.Send( "{HOME}+{END}" );
.
文本框的点击事件?或者甚至 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;
}
也适用于 Tab 键切换(通过文本框到文本框) - 在 Enter 中调用 SelectAll() 以防万一...
我使用的一行答案......你可能会踢自己......
在输入事件中:
txtFilter.BeginInvoke(new MethodInvoker( txtFilter.SelectAll));
'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;
}
对我来说,按 Tab 键进入控件会选择所有文本。
这是一个辅助函数,将解决方案提升到一个新的水平 - 无需继承即可重用。
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 事件中连接所有文本框。您可以将此函数放置在您的表单中,或者如果您像我一样,可以将其放置在实用程序类中的某个位置,以便更多地重用。
这适用于 WPF/XAML 文本框。
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;
}
这类似于 恩真里的流行答案,但我发现不必子类化更容易:
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
SelectAll 从来没有为我工作过。
这有效。
ActiveControl = textBox1;
textBox1->SelectionStart = 0;
textBox1->SelectionLength = textBox1->Text->Length;
我找到了一个更简单的解决方案:
要确保单击文本框时选择所有文本,请确保 Click 处理程序调用 Enter 处理程序。不需要额外的变量!
例子:
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();
}
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();
}
}
有趣的是,我认为具有 DropDownStyle=Simple 的 ComboBox 几乎具有您正在寻找的行为。
(如果您减小控件的高度以不显示列表 - 然后再减少几个像素 - 组合框和文本框之间没有有效的区别。)
为什么不直接使用文本框的 MouseDown-Event 呢?它对我来说效果很好,不需要额外的布尔值。非常干净和简单,例如:
private void textbox_MouseDown(object sender, MouseEventArgs e) {
if (textbox != null && !string.IsNullOrEmpty(textbox.Text))
{
textbox.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();
}
}
并在您的表单上使用它。
你有没有尝试过 MSDN 论坛“Windows Forms General”上建议的解决方案 它只是 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();
}
}
对于表单中的一组文本框:
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;
}
下面的似乎有效。Enter 事件处理对控件的 Tab 键切换,并且当单击控件时 MouseDown 起作用。
private ########### void textBox1_Enter(object sender, EventArgs e)
{
textBox1.SelectAll();
}
private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
if (textBox1.Focused)
textBox1.SelectAll();
}
我知道这个问题已经解决了,但我有一个我认为实际上相当简单的建议。
在鼠标松开事件中,您所要做的就是放置
if(textBox.SelectionLength = 0)
{
textBox.SelectAll();
}
它似乎在 VB.NET 中对我有用(我知道这是一个 C# 问题......可悲的是我被迫在工作中使用 VB..我遇到了这个问题,这就是我来到这里的原因......)
我还没发现有什么问题..除了它不会立即选择点击这一事实,但我遇到了问题......
以下解决方案对我有用。我添加了 OnKeyDown
和 OnKeyUp
事件覆盖以保持 TextBox 文本始终处于选中状态。
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 键,当您返回到控件时,所有文本都将被选中。
如果您通过鼠标进入,则插入符号将正确放置在您单击的位置。
private void maskedTextBox1_Leave(object sender, CancelEventArgs e)
{
maskedTextBox1.SelectAll();
}
答案实际上可能比上述所有答案都要简单,例如(在 WPF 中):
public void YourTextBox_MouseEnter(object sender, MouseEventArgs e)
{
YourTextBox.Focus();
YourTextBox.SelectAll();
}
当然,我不知道你想如何使用这段代码,但这里要查看的主要部分是:首先调用.Focus(),然后调用.SelectAll();
非常简单的解决方案:
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):
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 );
}
*实际上,以下序列最终会选择部分文本,但如果将鼠标移到文本框上,所有文本将再次被选择:
鼠标按下/文本选择/鼠标移出文本框/鼠标向上
只需在输入和单击事件上使用 selectall()
private void textBox1_Enter(object sender, EventArgs e)
{
textBox1.SelectAll();
}
private void textBox1_Click(object sender, EventArgs e)
{
textBox1.SelectAll();
}
我发现当鼠标单击而不立即释放时,此方法效果最好:
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;
}
我的解决方案非常原始,但可以很好地满足我的目的
private async void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
if (sender is TextBox)
{
await Task.Delay(100);
(sender as TextBox).SelectAll();
}
}
这在 .NET 2005 中对我有用 -
' * 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.