我正在将项目从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
。
答案 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));
}
感谢大家帮助我的尝试!