【问题标题】:WPF - Event / Binding - Infinite Loop IssueWPF - 事件/绑定 - 无限循环问题
【发布时间】:2015-03-24 12:37:09
【问题描述】:

我正在处理的一个 wpf 项目存在问题,其中两个控件似乎创建了一个无限循环。

我有两个接受输入的文本框(TextBox1 和 TextBox2)。当用户在 TextBox1 中输入一个数字时,一个事件会触发一个计算,然后该计算将向 TextBox2 填充一些值。 TextBox2 也可以接受输入,这将触发反向计算以填充 TextBox1 一些值。

由于这两个事件,当文本输入到 TextBox1 时,它似乎会触发一个无限循环,其中每个事件和视图模型更改都会不断触发另一个。

有谁知道防止这种情况发生的最佳方法?

下面是一些产生堆栈溢出的示例代码:

代码/视图模型:

public partial class HelloWorldView : UserControl, INotifyPropertyChanged {
    public HelloWorldView() {
        InitializeComponent();
        DataContext = this;
    }

    private decimal _fahrenheit = 32;
    public decimal Fahrenheit {
        get { return _fahrenheit; }
        set {
            _fahrenheit = value;
            OnPropertyChanged("Fahrenheit");
            Celsius = _fahrenheit / 2;
        }
    }

    private decimal _celsius;
    public decimal Celsius {
        get { return _celsius; }
        set {
            _celsius = value;
            OnPropertyChanged("Celsius");
        }
    }

    #region PropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

    #region Events

    private void FahrenheitBox_OnTextChanged(object sender, TextChangedEventArgs e) {
        _celsius = Fahrenheit / 2.1m;
        OnPropertyChanged("Celsius");
    }

    private void CelsiusBox_OnTextChanged(object sender, TextChangedEventArgs e) {
        _fahrenheit = Celsius * 1.2m;
        OnPropertyChanged("Fahrenheit");
    }

    #endregion
}     

Xaml:

<Grid>
    <StackPanel>
        <TextBlock Text="Hello World" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Calibri" FontSize="24" FontWeight="Bold"></TextBlock>

        <TextBox Name="FahrenheitBox" Text="{Binding Fahrenheit}" MaxWidth="75" TextChanged="FahrenheitBox_OnTextChanged" />

        <TextBox Name="CelsiusBox" Text="{Binding Celsius}" MaxWidth="75" TextChanged="CelsiusBox_OnTextChanged" />
    </StackPanel>
</Grid>

【问题讨论】:

  • 你能发布你的代码吗?
  • @adminSoftDK - 恐怕我做不到。我正在为一个工作项目这样做,规则禁止我发布任何内部代码。
  • 我已经修改了原始帖子以包含我的问题的示例。真的很感激如何解决这个问题。

标签: wpf wpf-controls


【解决方案1】:

我们的项目中也有这个问题,你需要停止相互更新,当你从 UI 设置 TextBox1 时,你的代码不应该为 TextBox2 属性调用 Setter 代码,你可以为该属性设置支持字段并单独引发 PropertyChanged 事件。

编辑除了您在事件处理程序中所做的更改之外,请更改您的视图模型代码。这是视图模型属性的一种非常常见的模式,用于防止不必要的事件。

public decimal Fahrenheit 
    {
        get { return _fahrenheit; }
        set {
                if(_fahrenheit == value)
                { 
                    return;
                }
                _fahrenheit = value;
                OnPropertyChanged(); // removed property name , not needed
                //Celsius = _fahrenheit / 2; // removed , allready taken care in event
            }
     }

    private decimal _celsius;
    public decimal Celsius 
    {
        get { return _celsius; }
        set {
                if(_celsius == value)
                {
                    return;
                }     
                _celsius = value;
                OnPropertyChanged(); // removed property name, not needed
             }
    }

【讨论】:

  • 这是唯一的方法吗?我在后面的 xaml 代码中设置了一堆状态,以便仅在尚未触发某些其他事件时触发事件。我觉得这应该是一个普遍的问题,应该有一个更优雅的解决方案。
  • 我想我可能不明白您在解释什么,但我尝试更改支持字段并单独提高 PropertyChanged,但它仍然在触发事件。我在原始帖子中附上了我的问题示例。它会产生一个 stackoverflow 错误。
【解决方案2】:

我没有测试过这个,但它应该像这样工作

public partial class HelloWorldView : UserControl, INotifyPropertyChanged {
public HelloWorldView() {
    InitializeComponent();
    DataContext = this;
}

private decimal _fahrenheit = 32;
public decimal Fahrenheit {
    get { return _fahrenheit; }
    set {
         if(_fahrenheit!=value)
       {
        _fahrenheit = value;
        Celsius = _fahrenheit / 2;
        OnPropertyChanged();//there is no need to pass a name, as your
      CallerMemberName deals with it
       }
    }
}

private decimal _celsius;
public decimal Celsius {
    get { return _celsius; }
    set {
        if(_celsius!=value)
       {
        _celsius = value;
        Fahrenheit = Celsius * 1.2m;
        OnPropertyChanged();
      }
    }
}

你根本不需要任何 OnTextChanged

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 2014-09-03
    • 2021-09-02
    • 2020-09-09
    • 2021-09-11
    • 2018-09-24
    相关资源
    最近更新 更多