2012-04-12 45 views
1

你如何讓你的對象線程安全的實現INotifyPropertyChanged?我不能使用SynchronizationContext,因爲我需要能夠序列化對象。如何在沒有SynchronizationContext的情況下使用INotifyPropertyChanged進行線程安全?

protected void OnPropertyChanged(string propertyName) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) 
      // What can I add here to make it thread-safe? 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
+1

「我不能使用SynchronizationContext,因爲我需要能夠序列化對象。」 - 這不應該是一個問題。你是否用'[NonSerialized]'標記了字段並添加了一個'OnDeserializing'方法來在反序列化時重置它? – hvd 2012-04-12 19:19:28

+0

我沒有。有沒有比在我創建對象時在SynchronizationContext中發送代碼時更容易的方法?或者我只是太懶惰了:D – Stephen 2012-04-12 19:23:24

+0

如果您在希望事件處理程序運行的相同上下文中創建對象,則可以在該對象的構造函數中將該字段設置爲「SynchronizationContext.Current」,否則,我不確定最好的辦法是什麼。 – hvd 2012-04-12 19:26:54

回答

6

所以......原來竟還有,如果你不介意依靠擴展在編譯生成一些代碼對你一個非常好的辦法時間。無論如何,我使用Fody/PropertyChanged,這是一個非常簡單的改變。這樣可以避免在真正沒有關於用戶界面的業務知識的模型中參考SynchronizationContext

  1. 首先,安裝PropertyChanged.Fody,可從NuGet獲得。

  2. 目前實施INofityPropertyChanged的每個班級應改爲具有[ImplementPropertyChanged]的屬性。應該刪除手動執行。有關更多信息,請參閱readmewiki

  3. A PropertyChangedNotificationInterceptor需要實施。他們爲wiki中的WPF提供了一個示例;我的WinForms項目實施:

    public static class PropertyChangedNotificationInterceptor 
    { 
        public static SynchronizationContext UIContext { get; set; } 
    
        public static void Intercept(object target, Action onPropertyChangedAction, string propertyName) 
        { 
         if (UIContext != null) 
         { 
          UIContext.Post(_ => 
          { 
           onPropertyChangedAction(); 
          }, null); 
         } 
         else 
         { 
          onPropertyChangedAction(); 
         } 
        } 
    } 
    
  4. PropertyChangedNotificationInterceptor.UIContext地方。你可以把PropertyChangedNotificationInterceptor.UIContext = WindowsFormsSynchronizationContext.Current;放在主窗體的構造函數中。

    Form constructor會經過Control constructor並最終最終會創建WindowsFormsSynchronizationContext(如果尚不存在)。因此,在這裏捕獲.Current是安全的。 (注:這可能是一個實現細節,並可能在未來的.NET版本,不同平臺等改變)

請記住,這僅適用於如果你只有一個單一的同步上下文(單UI線程)。如果你遇到過多個UI線程的混亂,需要多個數據綁定,這將變得更加複雜。所以請不要這樣做。

感謝Simon Cropp寫作PropertyChanged,也幫助我找到它的這個特殊功能。

3

如果您使用的是WPF,則可以使用分派器將您的調用編組到UI線程。

App.Current.Dispatcher.Invoke(new Action(()=>{ 
    if (handler != null) 
     handler(this, new PropertyChangedEventArgs(propertyName)); 
})); 
+0

不幸的是我必須使用Forms。 – Stephen 2012-04-12 19:55:57

+0

@Stephen你可以調用WinForms中的UI線程,通過調用控件上的調用,但這很笨重,不是很優雅 – 2012-04-12 20:11:23

+1

一小時的搜索...終於 - 一個優秀,乾淨,簡潔和工作的答案(無論如何wpf) 。謝謝! – Rob 2014-09-16 03:45:49

4

如果你不幸運,並且必須使用Winforms,請嘗試使用應用程序的MainForm來調用UI線程中的處理程序。壞的事情是,你必須包括

using System.Windows.Forms;

protected void OnPropertyChanged(string propertyName) 
{ 
    var handler = PropertyChanged; 
    if (handler != null) 
    { 
     if (Application.OpenForms.Count == 0) return; 
     var mainForm = Application.OpenForms[0]; 
     if(mainForm == null) return; // No main form - no calls 

     if (mainForm.InvokeRequired) 
     { 
      // We are not in UI Thread now 
      mainform.Invoke(handler, new object[] { 
       this, new PropertyChangedEventArgs(propName)}); 
     } 
     else 
     { 
      handler(this, new PropertyChangedEventArgs(propertyName)); 
     }    
    } 
} 
相關問題