WPF ObservableList Dispatcher线程

时间:2013-02-22 11:01:16

标签: c# .net wpf multithreading dispatcher

我正在将项目从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

3 个答案:

答案 0 :(得分: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 :(得分: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);
    });

}

答案 2 :(得分:0)

我找到了一个干净的解决方案here

这里可能出现的问题不是应该在更改上调用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));
}

感谢大家帮助我的尝试!