为什么这不会导致无限循环的事件?

时间:2014-11-19 10:35:32

标签: c# .net winforms events textchanged

我有一个简单的应用程序可以反转在另一个文本框中键入的任何文本。问题是,您可以修改任一文本框,更改将(字面上)反映在另一个文本框中。

我写了这段代码,相信它会引起问题。

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);
    }
}

我可能会这样做是为了安全,但必需来检查这个? (我甚至不会问是否推荐它。)

4 个答案:

答案 0 :(得分:30)

根据评论,并且已经回答,当您将TextChanged属性设置为已有的值时,不会引发Text事件。

目前尚不清楚这是否是您可以放心使用的。这是一个明智的优化,如果.NET Framework的未来版本放弃它,我会非常惊讶,但我不能说旧版本,也不能代表第三方实现(Mono)。

为了绝对安全,我使用你在问题中提出的Focused检查。我会完全 Text setter现在做的事情。

private void realText_TextChanged(object sender, EventArgs e)
{
    var newMirrorText = Mirror(realText.Text);
    if (mirrorText.Text != newMirrorText)
        mirrorText.Text = newMirrorText;
}

这具有防止无限递归的相同优点,但与您在表单中放置的其他代码相比可以更好地播放,这些代码会因其他事件而更改文本。

答案 1 :(得分:21)

它不会导致循环的原因是它检查Text属性是否实际更改,即新值是否等于旧值。在你的情况下,mirror函数恰好反转自身,两次传递后会导致相同的文本。

答案 2 :(得分:3)

很容易检查。

首先,用

替换两个文本框控件
    class T : TextBox
    {
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;
            }
        }
    }

其次,在setter上设置断点。将这些表达式添加到Watch窗口:

  • 名称
  • 文本

第三,启动应用程序,复制' 123'从某处将其粘贴到第一个文本框中。在这里:

第一次休息:

  • 姓名:" mirrorText"
  • 文字:""
  • 值:" 321"

第二次休息:

  • 姓名:" realText"
  • 文字:" 123"
  • 值:" 123"
第三......哎呀,它不会再破坏了。要探究为什么我们必须更深入。看一下referencesource:文本框设置器does nothing不寻常,但TextBoxBase的一个looks interesting

        set {
            if (value != base.Text) { // Gotcha!
                base.Text = value;
                if (IsHandleCreated) {
                    // clear the modified flag
                    SendMessage(NativeMethods.EM_SETMODIFY, 0, 0);
                }
            }
        }

因此,由于 hvd 已经回答,原因是如果旧值和新值相同,文本框不会引发TextChanged。我不认为这种行为会改变,至少对于winforms而言。但如果你想要更强大的解决方案,那么它就是:

    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");
    }

P.S。代理对上的mirror()会失败。 Here're一些解决方案。

答案 3 :(得分:-1)

如果textbox有Text,并且我们尝试使用相同的Text更改它,则TextChange事件不会引发,因为新文本与前一个相同。 在您的代码中,realText_TextChanged事件会反转文本并使用它更改mirrorText。 mirrorText_TextChanged事件反转文本并尝试更改realText。 realText已经有了这个文本,并没有引发realText_TextChanged事件。