C# WPF ObservableList分派器线程
我正在将一个项目从Windows窗体转换为WPF格式。目前,我已将所有数据绑定到元素。我现在在C# WPF ObservableList分派器线程,c#,.net,wpf,multithreading,dispatcher,C#,.net,Wpf,Multithreading,Dispatcher,我正在将一个项目从Windows窗体转换为WPF格式。目前,我已将所有数据绑定到元素。我现在在ObservableCollection中提出一个问题,说: 此类型的CollectionView不支持对其 来自与Dispatcher线程不同的线程的SourceCollection 如何使代码线程安全?或者如何引导对调度程序线程的更改?我看过一些关于它的帖子,但我对如何在我自己的项目中应用它感到困惑。也许有人能帮我解释一下 这是我的ObservableList.cs代码: public class
ObservableCollection
中提出一个问题,说:
此类型的CollectionView不支持对其
来自与Dispatcher线程不同的线程的SourceCollection
如何使代码线程安全?或者如何引导对调度程序线程的更改?我看过一些关于它的帖子,但我对如何在我自己的项目中应用它感到困惑。也许有人能帮我解释一下
这是我的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
}
现在,我所有的ObservableList都是
null
将项目添加到ObservableCollection
时,需要调用UI dispatcher线程来执行此操作
这是这样做的:
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" });
}
publicobservableList字符串{get;set;}
公共MyViewModel()
{
this.Strings=新的ObservableList();
this.Strings.AddRange(新的[]{“1”、“2”、“3”、“4”});
}
当您现在在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);
});
}
public void AddRange(IEnumerable items)
{
isInAddRange=true;
foreach(项目中的T项目)
{
如果(项!=null)
{
Dispatcher.CurrentDispatcher.Invoke((操作)(()=>
{
增加(项目);
}));
}
}
isInAddRange=false;
var e=new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,items.ToList());
Dispatcher.CurrentDispatcher.Invoke((操作)(()=>
{
基础。变更的集合(e);
});
}
我找到了一个干净的解决方案
这里可能出现的问题不是应该在更改时调用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
}
感谢大家帮助我!我还将推荐一种专门的方法来扩展您的解决方案,该方法可以添加一个项目,并同步类似于AddItemThreadSafe(myItem)的线程因此,当您添加来自多个源的项时,您不会得到多余的代码。您还可以考虑创建自己的ThreadSafeObservableCollection,它包装了normale observable collectionOk,因此我调用了UI线程。我必须先将lambda表达式强制转换为
Action
-类型。当我尝试运行此操作时,我获得了大量的nullpointer异常。至于Boas Enkler的评论,我喜欢创建一个额外的类来处理安全线程的想法。不过,我不知道从哪里开始,也不知道给定的解决方案为什么不起作用……我仔细研究了一下,似乎得到了一个observeList返回null
。这意味着添加项还没有完全起作用。让我来看看e更新我的问题以反映我自己所做的更改我已经更新了我的答案并测试了你的代码,在添加字符串时它似乎起作用。我已经更新了我的代码以反映你的问题,但我仍然收到一个nullpointer异常。创建的ObservableList为null
,因此我无法在其中添加项。如果不使用Invoke from Dis,则情况并非如此修补程序(但几秒钟后,应用程序因我的问题中所述的问题而崩溃)。问题是集合不是由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
}
/// <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));
}