2013-02-22 197 views
3

我將一個項目從Windows窗體轉換爲WPF格式。目前,我已將所有數據綁定到元素。我現在在ObservableCollection提出一個問題,他說:WPF ObservableList調度程序線程

這種類型的CollectionView不會從調度線程不同的線程支持更改其 SourceCollection。

如何讓我的代碼線程安全?或者,如何將更改引導到Dispatcher線程?我已經看到了一些關於它的文章,但我很困惑如何將它應用到我自己的項目上。也許有人可以爲我闡明這一點?

這是我的ObservableList.cs代碼:

public class ObservableList<T> : ObservableCollection<T> 
{ 
    #region Private members 

    bool isInAddRange = false; 

    #endregion Private members 

    #region Public methods 

    /// <summary> 
    /// Creates a new empty ObservableList of the provided type. 
    /// </summary> 
    public ObservableList() 
    { 

    } 

    /// <summary> 
    /// Handles the event when a collection has changed. 
    /// </summary> 
    /// <param name="e"></param> 
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     // intercept this when it gets called inside the AddRange method. 
     if (!isInAddRange) 
      base.OnCollectionChanged(e); 
    } 

    /// <summary> 
    /// Adds a collection of items to the ObservableList. 
    /// </summary> 
    /// <param name="items"></param> 
    public void AddRange(IEnumerable<T> items) 
    { 
     isInAddRange = true; 
     foreach (T item in items) 
     { 
      Add(item); 
     } 

     isInAddRange = false; 

     var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,items.ToList()); 
     base.OnCollectionChanged(e); 

    } 

    #endregion Public methods 
} 

}

編輯:答案通過YWM給出後,我改變了我的AddRange類:

public void AddRange(IEnumerable<T> items) 
{ 
    isInAddRange = true; 
    foreach (T item in items) 
    { 
     if (item != null) 
     { 
      Dispatcher.CurrentDispatcher.Invoke((Action)(() => 
       { 
        Add(item); 
       })); 
     } 
    } 

    isInAddRange = false; 

    var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,items.ToList()); 
    base.OnCollectionChanged(e); 

} 

現在,我所有的ObservableList的是null

回答

0

我已經找到了一個乾淨的解決方案here

這裏可能發生的問題不是應該在更改上調用UI線程,而是創建集合的線程!這並不一定是UI線程!

於是,我改變了我的代碼如下:

public class ObservableList<T> : ObservableCollection<T> 
{ 
    #region Private members 

    bool isInAddRange = false; 
    private readonly Dispatcher _currentDispatcher; 

    #endregion Private members 

    #region Public methods 

    /// <summary> 
    /// Creates a new empty ObservableList of the provided type. 
    /// </summary> 
    public ObservableList() 
    { 
     //Assign the current Dispatcher (owner of the collection) 
     _currentDispatcher = Dispatcher.CurrentDispatcher; 
    } 

    /// <summary> 
    /// Executes this action in the right thread 
    /// </summary> 
    ///<param name="action">The action which should be executed</param> 
    private void DoDispatchedAction(Action action) 
    { 
     if (_currentDispatcher.CheckAccess()) 
      action.Invoke(); 
     else 
      _currentDispatcher.Invoke(DispatcherPriority.DataBind, action); 
    } 

    /// <summary> 
    /// Handles the event when a collection has changed. 
    /// </summary> 
    /// <param name="e"></param> 
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     // intercept this when it gets called inside the AddRange method. 
     if (!isInAddRange) 
     { 
      DoDispatchedAction(() => base.OnCollectionChanged(e)); 
     } 
    } 

    /// <summary> 
    /// Adds a collection of items to the ObservableList. 
    /// </summary> 
    /// <param name="items"></param> 
    public void AddRange(IEnumerable<T> items) 
    { 
     isInAddRange = true; 
     foreach (T item in items) 
     { 
      if (item != null) 
       { 
        Add(item); 
      } 
     }  

     isInAddRange = false; 

     var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,items.ToList()); 
     DoDispatchedAction(() => base.OnCollectionChanged(e)); 

    } 

    #endregion Public methods 
} 

我承認我還沒有測試AddRange()方法本身呢。鏈接的網站上給出的樣本是:

/// <summary> 
/// Inserts a item at the specified index 
/// </summary> 
///<param name="index">The index where the item should be inserted</param> 
///<param name="item">The item which should be inserted</param> 
protected override void InsertItem(int index, T item) 
{ 
    DoDispatchedAction(() => base.InsertItem(index, item)); 
} 

感謝大家的努力來幫助我!

4

當您將項目添加到ObservableCollection時,您需要調用UI調度程序線程來執行此操作。在課堂上使用它

Dispatcher.CurrentDispatcher.Invoke(() => 
    { 
     foreach (var myModel in itemsToAdd) 
     { 
        Images.Add(mymodel);     
     } 
    }); 

然後,

public ObservableList<String> Strings { get; set; } 

    public MyViewModel() 
    { 
     this.Strings = new ObservableList<string>(); 

     this.Strings.AddRange(new[] { "1", "2", "3", "4" }); 
    } 
+1

我還要通過推薦增加了一個項目,syncrhonizes螺紋狀 AddItemThreadSafe(myItem項目)專用的方法擴展您的解決方案,所以你不要當你從多個來源添加項目獲得冗餘代碼。你也可以考慮創建自己的ThreadSafeObservableCollection ,它包裝了normale可觀察集合 – 2013-02-22 12:57:31

+0

好的,所以我調用了UI線程。我必須首先將lambda表達式轉換爲「Action」類型。當我試圖運行這個時,我獲得了很多空指針異常。至於Boas Enkler的評論,我喜歡創建一個額外的類來關注安全線程的想法。雖然,我不知道從哪裏開始,爲什麼給定的解決方案不起作用... – Joetjah 2013-02-22 13:19:28

+0

我看起來更多一點,它似乎得到一個ObservableList返回'null'。這意味着添加項目還沒有完成。讓我更新我的問題,以反映我自己做出的更改 – Joetjah 2013-02-22 13:23:40

0

雖然你現在呼籲在UI線程Add方法,你仍然引發事件:

這是像這樣做在調用線程的AddRange方法中。所以你最終會遇到同樣的問題,就像你做改變之前一樣。

試試這個:

public void AddRange(IEnumerable<T> items) 
{ 
    isInAddRange = true; 
    foreach (T item in items) 
    { 
     if (item != null) 
     { 
      Dispatcher.CurrentDispatcher.Invoke((Action)(() => 
       { 
        Add(item); 
       })); 
     } 
    } 

    isInAddRange = false; 

    var e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,items.ToList()); 
    Dispatcher.CurrentDispatcher.Invoke((Action)(() => 
    { 
     base.OnCollectionChanged(e); 
    }); 

} 
+0

我仍然收到相同的錯誤,不幸的是 – Joetjah 2013-02-25 12:36:23

+0

問題在於Collection不是由UI線程創建的,而是由其他線程創建的。查看我的回覆以獲取更多信息!感謝您的時間和幫助! – Joetjah 2013-02-25 13:08:45