事件TextChanged被多次触发。

4

我的Windows手机应用程序中有三个文本框。我想在用户输入任何一个文本框后立即更改这三个文本框的数值。

我的XAML如下:

<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"/>

我的C#代码如下:

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

我猜当我给文本框分配新值时,事件会再次触发,导致我的代码进入无限循环。我做错了什么?我该如何解决?

强烈建议您创建一个适当的ViewModel并使用适当的DataBinding,而不是使用这种可怕的丑陋代码。 - Federico Berasategui
谢谢,@HighCore,我会尝试这样做,但由于我对wpf和ViewModel还很陌生,您能否建议一些解决我的问题的方案? - Monica
2个回答

3
您的代码陷入了无限循环,因为您在TextChanged事件中更改了文本,导致它再次触发。 Rohit Vats的答案不起作用,因为TextChanged事件异步的 - 所以您的情况是竞争状态。为了更好地了解其工作原理,我通过添加SempahoreSlim并等待其他事件完成来改进了Rohit的答案。另一个问题是该事件仅在值更改时引发,因此我们必须检查是否等待信号量。
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();
}

上面的代码有些混乱,但应该能够展示正在发生的事情(不要使用它-它只是一个例子),进行调试和操作。

您还可以尝试通过取消订阅/订阅事件来简化它:

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

当然,你的代码可能需要修改,以避免出现这种情况。

谢谢!!很棒的答案,帮助我理解了很多东西... :) - Monica
1
@mara 你可能已经对这个问题了如指掌,但我在我的博客上详细讲解了它(它是我的第一篇文章)。也许这会有所帮助。祝一切顺利。 - Romasz

1
你需要使用某种形式的标记,比如 textChangedFromCode,并在尝试从处理程序设置文本时设置它,在运行代码之前检查其值。
伪代码如下:
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.
   }
}

这个不起作用,重置标志后仍然在调用“cambio”。 - Monica
是的,它仍然会调用 cambio,这就是为什么你必须在顶部放置额外的检查,就像我在答案中提到的那样 if(!textChangedFromCode),这样如果它从代码中被调用,你的代码块不会执行,因为我们已经将其包装在一个 if 条件下。 - Rohit Vats

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