4

I have three TextBox in my app on windows phone. I want to change the numeric value of these three TextBox right after the user inserts an input in any of them.

My xaml looks like this:

<TextBox x:Name="t_horizontal" InputScope="Number" TextChanged="cambio"/>
<TextBox x:Name="t_vertical" InputScope="Number" TextChanged="cambio" />
<TextBox x:Name="t_diagonal" InputScope="Number" TextChanged="cambio"/>

and my code in c# is:

private void cambio(object sender, TextChangedEventArgs e)
{
    TextBox modificado = sender as TextBox;
    if (modificado.Name == "t_horizontal")
    {
        this.ancho = Double.Parse(modificado.Text);
        this.diagonal = getDiagonal(this.ancho, this.alto);
    } 
    else if (modificado.Name == "t_vertical")
    {
        this.alto = Double.Parse(modificado.Text);
        this.diagonal = getDiagonal(this.ancho,this.alto);
    }
    else if (modificado.Name == "t_diagonal")
    {
        this.diagonal = Double.Parse(modificado.Text);
        this.ancho = getAncho(diagonal);
        this.alto = getAlto(diagonal);
    }
    t_vertical.Text = this.alto+"";
    t_horizontal.Text = this.ancho+"";
    t_diagonal.Text = this.diagonal+"";
}

I guess that when I assign a new value for the TextBoxes, the event is fired again, and my code enters in a infinite loop. What am I doing wrong? How can I solve this?

Monica
  • 311
  • 1
  • 8
  • 22
  • It's strongly recommended that you create a proper [ViewModel](http://stackoverflow.com/a/14382137/643085) and use proper [DataBinding](http://msdn.microsoft.com/en-us/library/ms752347(v=vs.110).aspx), as opposed to this horrible monstrosity. – Federico Berasategui Apr 08 '14 at 18:57
  • Thanks, @HighCore, I will try to do so, but as I'm new with wpf and ViewModel, would you suggest any solution to my problem with that new model? – Monica Apr 08 '14 at 19:21

2 Answers2

3

You are in an infinite loop beacause you change the text in TextChanged event so it is firing it once again. Rohit Vats answer won't work because TextChanged event is asynchronous - so your case is racy. To better see how it works, I've improved Rohit's answer by adding a SempahoreSlim and waiting for other event to finish. The other problem is that the event is raised only when the value has changed, so we have to check if to wait for the semaphore.

private bool textChanged = false;
SemaphoreSlim sem = new SemaphoreSlim(0, 1);
private async void cambio(object sender, TextChangedEventArgs e)
{
    if (!textChanged)
    {
        TextBox modificado = sender as TextBox;
        if (modificado.Name == "t_horizontal")
        {
            this.ancho = Double.Parse(modificado.Text);
            this.diagonal = getDiagonal(this.ancho, this.alto);
        }
        else if (modificado.Name == "t_vertical")
        {
            this.alto = Double.Parse(modificado.Text);
            this.diagonal = getDiagonal(this.ancho, this.alto);
        }
        else if (modificado.Name == "t_diagonal")
        {
            this.diagonal = Double.Parse(modificado.Text);
            this.ancho = getAncho(diagonal);
            this.alto = getAlto(diagonal);
        }
        textChanged = true;
        if (t_vertical.Text != this.alto + "")
        {
            t_vertical.Text = this.alto + "";
            await sem.WaitAsync(); // wait until finished changing with skip (flag)
        }
        if (t_horizontal.Text != this.ancho + "")
        {
            t_horizontal.Text = this.ancho + "";
            await sem.WaitAsync(); // wait until finished changing with skip (flag)
        }
        if (t_diagonal.Text != this.diagonal + "")
        {
            t_diagonal.Text = this.diagonal + "";
            await sem.WaitAsync(); // wait until finished changing with skip (flag)
        }
        textChanged = false;
    }
    else sem.Release();
}

Above code is dirty but should show what is happening (don't use it - it is only an example), debug and play with it.

You can also try to make it simpler - by unsubscribing/subscribing to events:

private void cambio(object sender, TextChangedEventArgs e)
{
    TextBox modificado = sender as TextBox;
    if (modificado.Name == "t_horizontal")
    {
        this.ancho = Double.Parse(modificado.Text);
        this.diagonal = getDiagonal(this.ancho, this.alto);
    }
    else if (modificado.Name == "t_vertical")
    {
        this.alto = Double.Parse(modificado.Text);
        this.diagonal = getDiagonal(this.ancho, this.alto);
    }
    else if (modificado.Name == "t_diagonal")
    {
        this.diagonal = Double.Parse(modificado.Text);
        this.ancho = getAncho(diagonal);
        this.alto = getAlto(diagonal);
    }
    t_vertical.TextChanged -= cambio;
    t_horizontal.TextChanged -= cambio;
    t_diagonal.TextChanged -= cambio;
    t_vertical.Text = this.alto + "";
    t_horizontal.Text = this.ancho + "";
    t_diagonal.Text = this.diagonal + "";
    t_vertical.TextChanged += cambio;
    t_horizontal.TextChanged += cambio;
    t_diagonal.TextChanged += cambio;
}

Of course your code probably should be modified to prevent from such a situation.

Community
  • 1
  • 1
Romasz
  • 29,662
  • 13
  • 79
  • 154
  • Thanks!! great answer, it helped me to understand a lot of things... :) – Monica Apr 09 '14 at 16:46
  • 1
    @mara You probably know everything about the problem, but I've [pulled it to pieces on my blog](http://www.romasz.net/watch-out-for-asynchronous-events/) (it turned out to be the very first post ;). Maybe it will help a little more. Best wishes. – Romasz Apr 10 '14 at 18:37
1

You have to use some sort of flag say textChangedFromCode and set it when you try to set Text from handler and check its value before running your code.

Pseudo code will look like this:

bool textChangedFromCode;
private void cambio(object sender, TextChangedEventArgs e)
{
   if (!textChangedFromCode) // Check for condition here.
   {
      ......
      textChangedFromCode= true; // Set the flag here before editing text value.

      t_vertical.Text = this.alto+"";
      t_horizontal.Text = this.ancho+"";
      t_diagonal.Text = this.diagonal+"";

      textChangedFromCode= false; // Reset the flag once done.
   }
}
Rohit Vats
  • 79,502
  • 12
  • 161
  • 185
  • This is not working, it's still calling again to `cambio` after reseting the flag. – Monica Apr 08 '14 at 18:37
  • Yeah it is will still call the `cambio`, that's why you have to put extra check at top like I mentioned in answer `if(!textChangedFromCode)` so that if it gets called from code, your piece of code doesn't execute since we have already wrap it under an if condition. – Rohit Vats Apr 10 '14 at 11:03