为什么这不会造成无限的事件循环呢?
我有一个简单的应用程序,反转任何文本input到另一个文本框中。 问题是,你可以修改任何一个文本框,这些改变将会被(反过来)反映在另一个文本框中。
我写这个代码,相信会导致问题。
private void realText_TextChanged(object sender, EventArgs e) { mirrorText.Text = mirror(realText.Text); } private void mirrorText_TextChanged(object sender, EventArgs e) { realText.Text = mirror(mirrorText.Text); } private string mirror(string text) { return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n"); }
然后我试了一下,认为它会导致无限循环( realText
更改mirrorText
,另一个事件发生, mirrorText
更改realText
等)。 然而,除了预期的行为发生之外,
我当然很高兴,我可以把它留在这里。 或者我可以吗?
我很确定TextChanged
事件应该被触发,每当Text
被改变。 这是事件中一些错误保护的预期行为,还是我只是幸运? 这个代码可能在其他计算机上运行不正常,还有其他的编译设置等? 它可以很容易地修复:
private void realText_TextChanged(object sender, EventArgs e) { if (realText.Focused) { mirrorText.Text = Mirror(realText.Text); } }
为了安全起见,我可能会这样做,但需要检查吗? (我甚至不会问是否推荐。)
根据注释,正如已经回答的那样,当您将Text
属性设置为已有的值时, TextChanged
事件不会被TextChanged
。
目前尚不清楚这是否可以安全依靠。 这是一个明智的优化,如果将来的.NET Framework版本放弃它,我会感到非常惊讶,但我不能说老版本,也不能为第三方实现(单声道)。
为了绝对安全,我不会在你的问题中使用Focused
检查。 我会做什么Text
设置现在做。
private void realText_TextChanged(object sender, EventArgs e) { var newMirrorText = Mirror(realText.Text); if (mirrorText.Text != newMirrorText) mirrorText.Text = newMirrorText; }
这与阻止无限recursion具有相同的优点,但可以更好地与其他代码一起使用,这些代码可以放在窗体中,从而将文本作为其他事件的结果进行更改。
它不会导致循环的原因是它检查Text
属性是否实际改变,即如果新值不等于旧值。 在你的情况下, mirror
函数恰好相反,这导致了两遍后相同的文本。
这很容易检查。
首先,replace两个文本框控件
class T : TextBox { public override string Text { get { return base.Text; } set { base.Text = value; } } }
其次,在setter上设置断点。 将这些expression式添加到Watch窗口中:
- 名称
- 文本
- 值
第三,启动应用程序,从某处复制“123”并将其粘贴到第一个文本框。 这里是:
第一次rest:
- 名称:“mirrorText”
- 文字:“”
- 价值:“321”
第二次rest:
- 名称:“realText”
- 文本:“123”
- 值:“123”
第三…哎呀,它不会中断了。 为了检测为什么我们必须更深入。 看参考文献:文本框设置器没有什么不寻常的,但TextBoxBase的一个看起来很有趣 :
set { if (value != base.Text) { // Gotcha! base.Text = value; if (IsHandleCreated) { // clear the modified flag SendMessage(NativeMethods.EM_SETMODIFY, 0, 0); } } }
所以,正如hvd已经回答,原因是文本框不会引发TextChanged如果新旧值是相同的。 我不认为这种行为会改变,至less对于WinForms来说。 但如果你想要更强大的解决scheme,这里是:
private void RunOnce(ref bool flag, Action callback) { if (!flag) { try { flag = true; callback(); } finally { flag = false; } } } private bool inMirror; private void realText_TextChanged(object sender, EventArgs e) { RunOnce(ref inMirror, () => { mirrorText.Text = mirror(realText.Text); }); } private void mirrorText_TextChanged(object sender, EventArgs e) { RunOnce(ref inMirror, () => { realText.Text = mirror(mirrorText.Text); }); } private string mirror(string text) { return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n"); }
PS镜像()将在代理对上失败。 这里有一些解决scheme。
如果文本框有一个文本,并且我们尝试使用相同的文本进行更改,则TextChange事件不会引发,因为新文本与前一个文本相同。 在你的代码中,realText_TextChanged事件反转了文本,并用它改变了mirrorText。 mirrorText_TextChanged事件反转文本,并尝试更改realText。 realText已经有了这个文本,并且不会引发realText_TextChanged事件。