Windows 窗体中 TextBox 的默认行为是在第一次通过 Tab 键获得焦点时突出显示所有文本,但如果单击它则不会。我们可以通过查看 TextBox 的 OnGotFocus() 覆盖在 Reflector 中看到这一点:
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
if (!this.selectionSet)
{
this.selectionSet = true;
if ((this.SelectionLength == 0) && (Control.MouseButtons == MouseButtons.None))
{
base.SelectAll();
}
}
}
正是 if 语句导致了我们不喜欢的行为。此外,雪上加霜的是,每当重新分配文本时,Text 属性的设置器都会盲目地重置 selectionSet 变量:
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
this.selectionSet = false;
}
}
因此,如果您有一个 TextBox 并在其中添加选项卡,则将选择所有文本。如果您单击它,突出显示将被删除,如果您重新进入它,您的插入符号位置(和零选择长度)将被保留。但是如果我们以编程方式设置新的Text,并再次tab进入TextBox,那么所有的文本都会再次被选中。
如果您像我一样发现这种行为令人讨厌且不一致,那么有两种方法可以解决这个问题。
第一个,可能也是最简单的,是通过在表单Load() 上调用DeselectAll() 以及每当Text 更改时简单地触发selectionSet 的设置:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.textBox2.SelectionStart = this.textBox2.Text.Length;
this.textBox2.DeselectAll();
}
(DeselectAll() 只是将SelectionLength 设置为零。实际上是SelectionStart 翻转了TextBox 的selectionSet 变量。在上述情况下,不需要调用DeselectAll(),因为我们是将开头设置为文本的结尾。但是如果我们将其设置为任何其他位置,例如文本的开头,那么调用它是一个好主意。)
更持久的方法是通过继承创建我们自己的具有所需行为的 TextBox:
public class NonSelectingTextBox : TextBox
{
// Base class has a selectionSet property, but its private.
// We need to shadow with our own variable. If true, this means
// "don't mess with the selection, the user did it."
private bool selectionSet;
protected override void OnGotFocus(EventArgs e)
{
bool needToDeselect = false;
// We don't want to avoid calling the base implementation
// completely. We mirror the logic that we are trying to avoid;
// if the base implementation will select all of the text, we
// set a boolean.
if (!this.selectionSet)
{
this.selectionSet = true;
if ((this.SelectionLength == 0) &&
(Control.MouseButtons == MouseButtons.None))
{
needToDeselect = true;
}
}
// Call the base implementation
base.OnGotFocus(e);
// Did we notice that the text was selected automatically? Let's
// de-select it and put the caret at the end.
if (needToDeselect)
{
this.SelectionStart = this.Text.Length;
this.DeselectAll();
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
// Update our copy of the variable since the
// base implementation will have flipped its back.
this.selectionSet = false;
}
}
}
您可能想不调用base.OnGotFocus(),但这样我们将失去基类Control 中的有用功能。而且您可能很想完全不去搞乱selectionSet 的废话,并且每次在 OnGotFocus() 中简单地取消选择文本,但是如果用户在该字段之外并返回,我们将失去用户的突出显示。
丑吗?完全正确。但事实就是如此。