事件循环性



我发现自己经常处于以下情况:

我有一个绑定到某些数据的用户控件。无论何时更新控件,都会更新基础数据。无论何时更新基础数据,都会更新控件。因此,很容易陷入一个永无止境的更新循环(控制更新数据、数据更新控制、控制更新数据等)

通常,我通过使用bool(例如updatedByUser)来解决这个问题,这样我就知道控件是通过编程更新的还是由用户更新的,然后我就可以决定是否触发事件来更新底层数据。这看起来不太整洁。

是否有一些处理此类情况的最佳实践?

编辑:我添加了以下代码示例,但我想我已经回答了自己的问题。。。?

public partial class View : UserControl
{
    private Model model = new Model();
    public View()
    {
        InitializeComponent();
    }
    public event EventHandler<Model> DataUpdated;
    public Model Model
    {
        get
        {
            return model;
        }
        set
        {
            if (value != null)
            {
                model = value;
                UpdateTextBoxes();
            }
        }
    }
    private void UpdateTextBoxes()
    {
        if (InvokeRequired)
        {
            Invoke(new Action(() => UpdateTextBoxes()));
        }
        else
        {
            textBox1.Text = model.Text1;
            textBox2.Text = model.Text2;
        }
    }
    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        model.Text1 = ((TextBox)sender).Text;
        OnModelUpdated();
    }
    private void textBox2_TextChanged(object sender, EventArgs e)
    {
        model.Text2 = ((TextBox)sender).Text;
        OnModelUpdated();
    }
    private void OnModelUpdated()
    {
        DataUpdated?.Invoke(this, model);
    }
}
public class Model
{
    public string Text1 { get; set; }
    public string Text2 { get; set; }
}
public class Presenter
{
    private Model model;
    private View view;
    public Presenter(Model model, View view)
    {
        this.model = model;
        this.view = view;
        view.DataUpdated += View_DataUpdated;
    }
    public Model Model
    {
        get
        {
            return model;
        }
        set
        {
            model = value;
            view.Model = model;
        }
    }
    private void View_DataUpdated(object sender, Model e)
    {
        //This is fine.
        model = e;
        //This causes the circular dependency.
        Model = e;
    }
}

一个选项是停止更新,以防数据自上次以来没有更改。例如,如果数据是类的形式,则可以检查数据是否与上次触发事件时的实例相同,如果是,则停止传播。

这就是许多MVVM框架在属性没有实际更改的情况下防止引发PropertyChanged事件的方法:

private string _someProperty = "";
public string SomeProperty
{
    get
    {
        return _someProperty;
    }
    set
    {
        if ( _someProperty != value )
        {
           _someProperty = value;
           RaisePropertyChanged();
        }
    }
}

您可以在Windows窗体中类似地实现此概念。

您要查找的是数据绑定。它允许您连接两个或多个属性,这样当一个属性更改时,其他属性将自动神奇地更新。

在WinForms中,它有点丑陋,但在像你这样的情况下,它就像一种魅力。首先,您需要一个表示数据并实现INotifyPropertyChanged的类,以便在数据更改时通知控件。

public class ViewModel : INotifyPropertyChanged
{
    private string _textFieldValue;
    public string TextFieldValue {
        get
        {
            return _textFieldValue;
        }
        set
        {
            _textFieldValue = value;
            NotifyChanged();
        }
    }
    public void NotifyChanged()
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(null));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

Form/Control中,您将ViewModel.TextFieldValue的值绑定到textBox.Text。这意味着每当TextFieldValue的值改变时,Text属性将被更新,而每当Text属性改变时,则TextFieldValue将被更新。换句话说,这两个属性的值将是相同的。这解决了您遇到的循环问题。

public partial class Form1 : Form
{
    public ViewModel ViewModel = new ViewModel();
    public Form1()
    {
        InitializeComponent();
        // Connect:  textBox1.Text <-> viewModel.TextFieldValue
        textBox1.DataBindings.Add("Text", ViewModel , "TextFieldValue");
    }
}

如果您需要从窗体/控件外部修改值,只需设置ViewModel 的值

form.ViewModel.TextFieldValue = "new value";

控件将自动更新。

您应该研究MVP——它是Winforms UI的首选设计模式。http://www.codeproject.com/Articles/14660/WinForms-Model-View-Presenter

使用该设计模式除了可以避免循环事件之外,还可以为您提供更可读的代码。

为了真正避免循环事件,视图应该只导出一个属性,一旦设置了该属性,就会确保不会调用txtChanged_Event。

像这样的东西:

public string UserName
    {
        get
        {
            return txtUserName.Text;
        }
        set
        {
            txtUserName.TextChanged -= txtUserName_TextChanged;
            txtUserName.Text = value;
            txtUserName.TextChanged += txtUserName_TextChanged;
        }
    }

或者您可以将MZetko的答案与私有财产一起使用

相关内容

  • 没有找到相关文章

最新更新