.NET: TextBox的TextChanged事件有时候不会触发,尽管KeyUp和KeyDown事件已经触发了。

4
我遇到了一个非常奇怪的问题。我发现有时在输入文本框时,会丢失一些按键。我在这个文本框的事件中添加了一堆跟踪语句,发现当我丢失按键时,KeyUp、KeyDown和KeyPress事件都正确触发了,但TextChanged事件却没有触发。
有人知道为什么会出现这种情况吗?我可以把它归结为“.NET bug”,但我更想找出是否有解决方案。
如果有人建议我使用KeyUp/KeyDown事件来确定文本是否已更改,那也存在一个问题。KeyUp/KeyDown对于每次按键都会被调用多次,因此很难确定某人是否多次键入相同的字母。

你用了哪些按键来体验这个功能?TextChanged是文本框属性Text的一部分,任何输入(包括字母、数字和符号如$)都会触发TextChanged事件,箭头键、插入、覆盖不会触发TextChanged事件,但可以被KeyUp/KeyDown事件捕获。 - t0mm13b
一个明显的例子是在文本框为空时按退格键。没有TextChange事件,但有所有键盘事件。 - Hans Passant
这种情况发生时,我肯定是在使用普通的字母数字按键。 - dreadpirateryan
我修改了你的标题,因为除了你恰好使用C#编写.NET代码之外,这与C#无关。 - John Saunders
当我在文本框中按下一个字符的退格键时,它会从一个字符变为空白(这又是文本值的更改),我也遇到了同样的问题。当我选择文本框中的所有文本并按下删除键时,它也不起作用。 - IsmailS
3个回答

3
这可能有点突兀,但是你说过你已经设置了KeyUp、KeyDown和KeyPress事件处理程序,你有将e.Handled标志设置为true吗?请看这里:
private void textBox1_KeyDown(object sender, KeyEventArgs e) { e.Handled = true; }
private void textBox1_KeyUp(object sender, KeyEventArgs e) { e.Handled = true; }
private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { e.Handled = true; }
请在MSDN中查看有关此Handled属性的信息。(如果您本地安装了MSDN 2008 SP 1,则链接为ms-help://MS.MSDNQTR.v90.en/fxref_system.windows.forms/html/dfc80b44-1d79-6315-cbea-1388a048c018.htm)
引用一下:
Handled在Windows Forms中由不同的控件以不同的方式实现。 对于像TextBox这样的子类本地Win32控件的控件,它被解释为不应将关键消息传递给底层本地控件。
如果您在TextBox上设置了Handled为true,则该控件将不会将按键事件传递给底层Win32文本框控件,但它仍将显示用户键入的字符。
也许它没有被设置,即e.Handled = false;从而防止TextChanged事件触发?
您能检查并确认一下吗?
编辑:在dreadprivateryan的回复后,我可以怀疑(由于未发布代码),基于他的回复,当按Enter键时e.Handled为true,而其他情况则为false,这在我看来是导致不再接受进一步按键的原因。
  • 你是否正在尝试在按下Enter键时将焦点设置到另一个控件?可能是KeyUp和KeyDown发生冲突...
  • 移除键盘钩子并禁用它...
  • 我的建议是完全改变代码,如下所示,删除KeyDown或KeyUp事件处理程序之一,因为它们简单地说是相同的,技术上讲,分别指定了按下键时和释放键时。请看这个链接here。在SO上发布了一个类似的问题here

在下面的示例中,我使用keyUp事件处理程序在按下enter键时切换到下一个可用控件。在KeyPress事件处理程序中,这只是过滤输入,仅允许数字0-9,其他任何内容都被丢弃。该事件处理程序中还包括允许使用退格键进行编辑。

私有 void textBox1_KeyUp(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Enter) SendKeys.Send("{TAB}");
}
私有常量字符串 VALID_KEYS = "0123456789";
私有 void textBox1_KeyPress(object sender, KeyPressEventArgs e) 
{                  
    if (VALID_KEYS.IndexOf(char.ToUpper(e.KeyChar)) != -1 || e.KeyChar == (char)8) 
         e.Handled = false;                                                               
    else                                                                                 
         e.Handled = true;                                                                
}                                                                                        

谢谢您的建议。我只在按下回车键时设置e.Handled=true,否则我根本不设置handled。不幸的是,我认为这与我的问题无关 - 无论我是否按Enter,我都会丢失击键。 - dreadpirateryan

1

我其实不知道,但我的猜测是:你正在虚拟机中运行?

一个方法是设置一个计时器,读取文本并将其与之前输入的值进行比较。当它不等于之前检查的值时,调用事件处理程序代码。当你需要使用最终输入的值时,做一个额外的检查,以防计时器还没有触发。


不,它没有在虚拟机中运行。我会考虑计时器的想法——好建议。 - dreadpirateryan

0

你的意思是按键实际上丢失了,从未显示在框中?还是说你的意思是每次按键都没有收到TextChanged事件?

我相信TextChanged事件是由操作系统的EN_CHANGE通知驱动的,该通知通过WM_COMMAND消息发送。我知道Windows中某些类型的消息会被“合并”,以避免冗余通知。例如,WM_MOUSEMOVE消息可能会发生这种情况,这就是为什么您不会收到鼠标移动事件,而是跨越屏幕移动的每个像素。

我不能确定,但我认为TextChanged事件也会表现出这种方式。不过,我可以说备用输入方法也会产生这种副作用。当使用平板电脑输入面板时,文本框将不会为每个字符收到TextChanged通知。


实际上,密钥已丢失并且从未出现在框中 - 当发生这种情况时,我注意到TextChanged事件也不会触发。感谢有关Windows消息的信息。也许我可以在KeyUp / KeyDown事件期间手动触发该事件? - dreadpirateryan
1
似乎有些东西正在吞噬消息。也许某个地方安装了系统范围的消息钩子出了问题?或者我敢说,可能是某种键盘记录器?如果您已经安装了Spy ++,可以尝试运行它并查看消息是否到达窗口。 - Josh
程序中确实有一个键盘钩子——这是我应该包含的重要信息。使用以下代码创建钩子:SetWindowsHookEx(WH_KEYBOARD, KeyFunction, dllInstance, AppDomain.GetCurrentThreadId());KeyFunction方法检查某些按键,根据这些按键调用其他函数,然后调用CallNextHookEx函数。我的跟踪语句告诉我,在我丢失按键的情况下,钩子确实看到了按键,并且CallNextHookEx函数被正确调用。 - dreadpirateryan
1
你能确认一下没有钩子时键盘输入不会丢失吗?也许你可以发布一段关于钩子程序的代码片段。 - Josh

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