3

我正在将项目从 Windows 窗体转换为 WPF 格式。目前,我已将所有数据绑定到元素。我现在提出一个问题ObservableCollection,说:

这种类型的 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 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.

4

3 回答 3

4

当您向 an 添加项目时,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" });
    }
于 2013-02-22T12:55:11.160 回答
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);
    });

}
于 2013-02-22T16:43:30.183 回答
0

我在这里找到了一个干净的解决方案。

这里可能出现的问题不是应该在更改时调用 UI 线程,而是创建 Collection 的线程!这不一定是 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));
}

感谢大家尝试帮助我!

于 2013-02-25T13:07:32.633 回答