当窗体显示时,为什么文本框中的文本会被高亮(选中)?

101

我有一个包含一个 TextBox 的表单,在C#中,我通过以下方式将其设置为字符串:

textBox.Text = str;
被显示时,为什么文本框中的文本会出现高亮/选中状态?

你的问题可能与https://dev59.com/73M_5IYBdhLWcg3w-4jw有关。 - DarenW
你解决了吗?你是怎么修复的? - fletcher
@fletcher:我还没有时间去看它。我会在几天内选择最佳答案。 - CJ7
你可以添加一个vb.net标签,因为问题实际上是相同的,接受的答案也是有效的。 - Andrea Antonangeli
BenSmith的答案关于查看选项卡顺序在这种情况下将非常有用。 - Samitha Chathuranga
7个回答

149

文本框具有 TabIndex 值为0且 TabStop 属性设置为true。这意味着当表单显示时,该控件将获得焦点。

您可以将另一个控件设为0的 TabIndex 值(如果有的话),并给文本框分配不同的 TabIndex(>0),或者将 TabStop 属性设置为false来停止此操作。


1
你确定文本框的TabIndex属性被设置为0了吗?它是否出现了异常行为? - 26071986
@26071986 - 嗯,我进行了一个快速测试。如果在一个只有一个文本框和一个按钮的表单中,在构造函数中将文本框内的文本更改为当tabindex设置为0时,文本会被突出显示。如果按钮具有0 tab index并且文本框tabindex> 0,则不会突出显示文本。 - fletcher
看起来确实与TabIndex有关 - 只是我已经适当地更改了所有元素的tab索引(至少我认为是这样)。结果发现,组也有tab索引,您需要更改它们以及它们包含的所有元素。因此,虽然我已经设置了1-9号元素的tab,但某个组仍然有0号tab,因此该组中的文本框成为第一个激活的元素(因此其内容被突出显示)。 - deed02392
1
这并不一定与TabIndex = 0有关,但如果TextBox在表单中具有最低的TabIndex,则肯定会发生。要验证:在TextBox中设置TabIndex = 5,并在表单的所有其他控件的TabIndex中设置大于5的数字。 - Andrea Antonangeli
当您在TabControl中选择一个新的TabPage时,也会发生这种情况。同样的解决方案适用。 - JonP
谢谢!我只需要将TabStop属性从True更改为False,然后一切问题都得到解决了。 - ewu11

48
在Windows Forms中,TextBox的默认行为是如果通过按Tab键首次聚焦到它,将会突出显示所有文本,但是如果通过点击方式聚焦,则不会突出显示。我们可以通过查看TextBox的OnGotFocus()覆盖来了解这一点。请注意保留HTML标签。
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属性的setter都会盲目重置selectionSet变量:

public override string Text
{
    get
    {
        return base.Text;
    }
    set
    {
        base.Text = value;
        this.selectionSet = false;
    }
}

如果您有一个文本框,并将其切换到选中状态,那么所有文本都将被选择。如果您单击其中一个文本框,则高亮显示将被移除;如果您重新切换到该文本框,则光标位置(和选择长度为零)将被保留。但是,如果我们以编程方式设置了新的Text,然后再次切换到文本框中,那么所有文本都将再次被选择。
如果您像我一样觉得这种行为很烦人且不一致,那么有两种方法可以解决这个问题。
第一种方法,也可能是最简单的方法,就是通过在表单Load()和每当Text更改时调用DeselectAll()来触发selectionSet的设置:
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    this.textBox2.SelectionStart = this.textBox2.Text.Length;
    this.textBox2.DeselectAll();
}

(DeselectAll()仅将SelectionLength设置为零。实际上,是SelectionStart翻转了TextBoxselectionSet变量。在上述情况下,调用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()中取消选择文本,但这样如果用户从字段中切换出去再切回来,我们就会丢失用户的高亮。

丑陋?当然。但它就是它。


35
这个问题的答案帮助我解决了类似的问题,但简单的答案只是在很多其他复杂的建议中略有提及。只需在设置文本后将SelectionStart设置为0即可解决问题!
例如:
yourtextbox.Text = "asdf";
yourtextbox.SelectionStart = 0;

4
您可以通过打开以下选项来选择表单控件的选项卡顺序:
视图->选项卡顺序
请注意,如果您已经打开了表单设计视图,则此选项仅在“视图”中可用。
选择“选项卡顺序”会打开一个表单视图,允许您通过单击控件来选择所需的选项卡顺序。

1
这对我很有帮助。实际上,如果我们关注选项卡顺序,选项卡索引并不重要。 - Samitha Chathuranga

1

如果您想取消文本字段的高亮显示,可以在VS 2013中尝试使用以下初始化:

myTextBox.GotFocus += new System.EventHandler(this.myTextBox_GotFocus);

并添加该方法:
public void myTextBox_GotFocus(object sender, EventArgs e)
{
    myTextBox.SelectionLength=0;
}

如果您之前聚焦于文本框,选择其中的一些文本,然后移开焦点,再次聚焦于文本框,这将导致文本被取消选择。 - Stewart

0

我没有在C#上测试过这个,但是我在使用C++ WIN32对话框时遇到了同样的问题。似乎你可以通过从OnInitDialog()WM_INITDIALOG返回FALSE来改变行为。希望这能帮到你。


1
我认为这并没有太大帮助,因为Windows API被封装在WinForms内部。 - Nathan A

0
这是对我有用的内容
public void SetNotes(string notes)
    {
        notesTextBox.Text = notes;

        notesTextBox.Select();
        notesTextBox.SelectionLength = 0;
        notesTextBox.SelectionStart = notes.Length;//place cursor at end of text
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接