2016-07-25 104 views
3

我經常在以下情況發現自己:事件循環性

我有一個用戶控件綁定到某些數據。每當控件更新時,底層數據都會更新。每當底層數據更新時,控件就會更新。所以很容易陷入永無止境的更新循環(控制更新數據,數據更新控制,控制更新數據等)。

通常我通過使用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; 
    } 
} 
+0

你使用的是什麼UI框架? WPF? UWP?的WinForms?每個人都有自己的首選方法 - 但通常歸結爲不同類型的MVC設計模式 – gilmishal

+0

@gilmishal對不起,我忘了提及Winforms。 –

+0

我想如果你展示一些創建循環更新的代碼 - 將更容易找到更合適的解決方案 – Fabio

回答

1

你在找什麼叫Data Binding。它允許你連接兩個或更多屬性,這樣當一個屬性更改時,其他屬性將自動更新。

在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.TextFieldValuetextBox.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"; 

控制的值將自動更新。

+0

這看起來像我需要的東西。如果我有一個比字符串更復雜的屬性,比如另一個有多個屬性的類,我是否認爲只有當屬性被設置時纔會工作?例如,如果我執行以下操作:form.ViewModel.ComplexProperty.FirstProperty =「new value」;那麼NotifyChanged()將不會被觸發,因爲ComplexProperty是通過Get訪問器訪問的,而不是Set訪問器? –

+1

'ComplexProperty'的類型也應該實現'INotifyPropertyChanged'。 –

2

一種選擇是停止更新以防上次更改數據。例如,如果數據是以類的形式出現的,則可以檢查數據是否與最後一次事件觸發時的實例相同,如果是這種情況,請停止傳播。

這是很多MVVM框架做以防止在提高的情況下PropertyChanged事件的性質並沒有真正改變:

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

你同樣可以實現這個概念Windows窗體。

+0

這似乎很好,很簡單。什麼時候我的財產實際上是一個更復雜的類,本身有很多屬性?我想我需要重寫Equals()和==以比較單個字段。 –

+0

@digital_fate不,你需要做一個簡單的比較,因爲你想阻止屬性更改事件觸發,並且與子屬性的內容無關。 – Maarten

+0

如果實例本身相同並且只有子屬性發生更改,那麼您需要按屬性執行比較屬性。 –

0

你應該看看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's回答了私人財產

+0

爲什麼它會避免循環事件? – Maarten

+0

我仍然可以看到以下情況:用戶更新視圖 - >演示者更新模型 - >模型更新 - >演示者更新視圖 - >等等。我或者誤解了這種模式,或者需要某種方式讓演示者或者演示者查看是否啓動事件? –

+1

好吧,它將視圖從底層數據中分離出來,如果正確實現,它只會在調用dataChanged事件時更新底層數據,然後纔會更新底層數據。一般來說,您正在爲這兩種不同的情況訂閱不同的方法,並作出相應的響應, 查看引用鏈接中的項目 - 他沒有收到循環事件問題。 – gilmishal