Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't this causing an infinite loop of events?

Per the comments, and as already answered, the TextChanged event is not getting raised when you set the Text property to the value it already has.

It's not clear whether this is something you can safely rely upon. It is a sensible optimisation, and I would be very surprised if future versions of .NET Framework drop it, but I cannot speak for older versions, nor for third-party implementations (Mono).

To be absolutely safe, I would not use the Focused check you put in your question. I would do exactly what the Text setter does now.

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

This has the same advantage of preventing infinite recursion, but plays more nicely with other code you may put in your form that changes the text as a result of some other event.


The reason it doesn't cause a loop is that it checks whether the Text property actually changed, i.e. if the new value does not equal the old value. In your case the mirror function happens to reverse itself, which leads to the same text after two passes.


It's pretty easy to check.

First, replace both textbox controls with

    class T : TextBox
    {
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;
            }
        }
    }

Second, set the breakpoint on setter. Add these expressions to the Watch window:

  • Name
  • Text
  • value

Third, launch the app, copy '123' from somewhere and paste it to the first textbox. Here it goes:

1st break:

  • Name: "mirrorText"
  • Text: ""
  • value: "321"

2nd break:

  • Name: "realText"
  • Text: "123"
  • value: "123"

3rd... whoops, it does not breaks anymore. To detect why we had to go deeper. Look at referencesource: text box setter does nothing unusual, but TextBoxBase's one looks interesting:

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

So, as hvd already answered, the reason is the textbox does not raise TextChanged if old and new values are the same. I don't think the behavior will change, at least for winforms. But if you want more robust solution, here it is:

    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() will fail on surrogate pairs. Here're some solutions.