2009-12-10 97 views
6

INotifyPropertyChanged的我有一個有螺紋

BindingList<T> 

它綁定到一個DataGridView。我班的一個房產需要很長的時間來計算,所以我對這個行爲進行了調整。計算後,我提出OnPropertyChanged()事件來通知網格該值已準備就緒。

至少,這是理論。但是由於OnPropertyChanged方法是從一個不同的線程調用的,所以我在網格的OnRowPrePaint方法中得到了一些有意識的異常。

任何人都可以告訴我如何在主線程中執行OnPropertyChanged事件嗎?我不能使用Form.Invoke,因爲類MyClass不知道它在Winforms應用程序中運行。

public class MyClass : INotifyPropertyChanged 
{ 
    public int FastMember {get;set;} 

    private int? slowMember; 
    public SlowMember 
    { 
     get 
     { 
      if (slowMember.HasValue) 
       return slowMember.Value; 
      else 
      { 
       Thread t = new Thread(getSlowMember); 
       t.Start(); 
       return -1; 
      } 

     } 
    } 

    private void getSlowMember() 
    { 
     Thread.Sleep(1000); 
     slowMember = 5; 
     OnPropertyChanged("SlowMember"); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string propertyName) 
    { 
     PropertyChangingEventHandler eh = PropertyChanging; 
     if (eh != null) 
     { 
      eh(this, e); 
     } 
    } 

} 

回答

8

通過設計,控件只能通過它創建的線程來更新。這就是爲什麼你會收到異常。

考慮使用BackgroundWorker,並且只有通過訂閱事件處理程序到RunWorkerCompleted才能在持久操作完成後更新成員。

+0

工程就像一個魅力。直到現在我還不知道BackgroundWorker。 這使得這個任務如此簡單,thx很多。 – 2009-12-10 12:59:45

1

考慮1:
看看UIThreadMarshal類及其用法在這篇文章中:
UI Thread Marshaling in the Model Layer
您可以從靜態類更改爲實例,並將其注入到你的對象。所以你的對象不會知道Form類。它只會知道關於UIThreadMarshal類。

代價2:
我不認爲從您的財產返回-1是個好主意。這對我來說看起來不太合適。

考慮3:
也許你的班級不應該使用antoher線程。也許是消費者類應該決定如何調用你的財產:直接或在一個單獨的線程。在這種情況下,您可能需要提供其他屬性,例如IsSlowMemberInitialized。

+0

致1:感謝您的鏈接。 BackgroundWorker在這種情況下解決了我的問題,但我敢肯定我的短褲在不久的將來我需要這個。 2:你說得對,尤其是因爲SlowMember可以是-1。僅用於測試 至3:不可能,因爲DataGridView查詢值(並且首次獲取-1,比我更新該值並使用INotifyPropertyChanged接口通知datagridview更改了屬性,必須(確定我可以使用Timer並檢查IsSlowMemberInitialized = true,但這很難看) 無論如何thx很多 – 2009-12-11 14:16:57

+0

如果你使用DataGridView,那麼你可能需要使用BindingSource。在我給你的鏈接中,有一個支持BindingSource的實現,它支持來自不同線程的綁定,你可以使用該代碼來更好地滿足你的需求。 – nightcoder 2009-12-11 22:07:16

2

這是我前一段時間寫的東西;它應該工作合理不錯,但要注意很多更新的成本...

using System.ComponentModel; 
using System.Threading; 
public class ThreadedBindingList<T> : BindingList<T> { 
    SynchronizationContext ctx = SynchronizationContext.Current; 
    protected override void OnAddingNew(AddingNewEventArgs e) { 
     if (ctx == null) { BaseAddingNew(e); } 
     else { ctx.Send(delegate { BaseAddingNew(e); }, null); } 
    } 
    protected override void OnListChanged(ListChangedEventArgs e) { 
     if (ctx == null) { BaseListChanged(e); } 
     else { ctx.Send(delegate { BaseListChanged(e); }, null); } 
    } 
    void BaseListChanged(ListChangedEventArgs e) { base.OnListChanged(e); } 
    void BaseAddingNew(AddingNewEventArgs e) { base.OnAddingNew(e); } 
} 
+0

有趣的實現Marc,但是由於這允許糟糕的設計,我認爲它應該只用於某些場景在處理動作時,實際需要控制權來更新。 – 2009-12-10 13:13:21

6

人們有時會忘記該事件處理程序是MultiCastDelegate並且,正因爲如此,有所有關於每個用戶的信息,我們需要適度地處理這種情況,而不會不必要地強加「調用+同步」性能損失。我一直在使用這樣的代碼適用年齡:

using System.ComponentModel; 
// ... 

public event PropertyChangedEventHandler PropertyChanged; 

protected virtual void OnPropertyChanged(string propertyName) 
{ 
    var handler = PropertyChanged; 
    if (handler != null) 
    { 
     var e = new PropertyChangedEventArgs(propertyName); 
     foreach (EventHandler h in handler.GetInvocationList()) 
     { 
      var synch = h.Target as ISynchronizeInvoke; 
      if (synch != null && synch.InvokeRequired) 
       synch.Invoke(h, new object[] { this, e }); 
      else 
       h(this, e); 
     } 
    } 
} 

它能做什麼是簡單的,但我記得我幾乎打斷了我的腦回,然後試圖找到做到這一點的最好辦法。

它首先「抓住」本地屬性上的事件處理函數以避免任何競爭條件。

如果處理程序不爲空(至少有一個訂戶確實存在),它將準備事件參數,然後遍歷此多播委託的調用列表。

調用列表具有目標屬性,它是事件的訂閱者。如果這個訂閱者實現了ISynchronizeInvoke(所有的UI控件實現它),我們然後檢查它的InvokeRequired屬性,我們只是調用它來傳遞委託和參數。以這種方式調用它會將調用同步到UI線程中。

否則,我們直接調用事件處理程序委託。

+2

我不得不將'EventHandler'重命名爲'PropertyChangedEventHandler',因爲我得到了一個'System.InvalidCastException',其詳細信息爲{{「無法強制類型爲'System.ComponentModel.PropertyChangedEventHandler'的對象來鍵入'System.EventHandler'。」 }' 我有一個BindingList是在訂閱內部事件的UI線程中創建的,但同步變量總是返回null,因爲h.Target爲null。 – 2016-08-23 00:26:04

+0

我遇到了與@RickShealer相同的問題。注意到日期我想知道這是否是新版本.Net的問題?這對於交叉線程INotifyPropertyChanged問題來說似乎是一個非常優雅的解決方案,所以我希望我們能夠使其發揮作用。 – Jacob 2016-12-20 17:40:48

+0

@Jacob我會爲新的框架定位,看它是否失敗。你告訴我你的項目的框架目標版本或任何其他信息,你可能認爲是相關的? – Loudenvier 2016-12-20 18:35:29