我正在使用WPF应用程序,在使用ICollectionView向ObservableCollection添加项时遇到了一个问题。我有一个IOPortsViewModel类,它包含一个名为DataPorts的ObservableCollection。在这个类的构造函数中,我使用CollectionViewSource.GetDefaultView(DataPorts)创建了一个名为CollectionDataPorts的ICollectionView示例。
在我的MainWindowViewModel类中,我有一个名为ReloadMainTreeViewSeparateThread()的方法,它在一个单独的工作线程中创建一些新的MenuItem对象,并将它们添加到一个名为MenuItemsLeft的ObservableCollection中,该集合在UI线程上通过Dispatcher定义。这个很好用。
但是,当我尝试从UI线程向CollectionDataPorts添加项时,我得到以下错误:
System.NotSupportedException: 'This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.'
如果我直接绑定到IOPortsView中的DataPorts而不是CollectionDataPorts,一切都正常。
下面是我的代码以供参考:
internal class IOPortsViewModel : ObservableObject
{
public IOPortsViewModel()
{
CollectionDataPorts = CollectionViewSource.GetDefaultView(DataPorts);
}
private ICollectionView m_collectionDataPorts;
public ICollectionView CollectionDataPorts
{
get => m_collectionDataPorts;
set => SetProperty(ref m_collectionDataPorts, value);
}
private ObservableCollection<VMDataPorts> m_dataPorts;
public ObservableCollection<VMDataPorts> DataPorts
{
get => m_dataPorts;
set => SetProperty(ref m_dataPorts, value);
}
}
IOPortsView:
<DataGrid Name="dataGrid" ItemsSource="{Binding CollectionDataPorts}">
internal class MainWindowViewModel : CommunityToolkit.Mvvm.ComponentModel.ObservableObject
{
ObservableCollection<MenuItem> MenuItemsLeft = new();
public void ReloadMainTreeViewSeparateThread()
{
Thread t = new Thread(() =>
{
App.Current.Dispatcher.Invoke(() => { IsDialogLoadingOpen = true; });
try
{
ObservableCollection<MenuItem> NewProcessedLeftMenuTree = new()
{
new MenuItem ( new IOPortsViewModel(), "Title"),
};
//Adding menuItems into UI thread which were created on this worker thread (Thread t)
App.Current.Dispatcher.Invoke(()=>
{
foreach (MenuItem menuItem in NewProcessedLeftMenuTree)
{
MenuItemsLeft.Add(menuItem);
}
});
}
catch (AggregateException aggrEx)
{
App.Current.Dispatcher.BeginInvoke(() =>
{
IsDialogLogOpen = true;
});
}
finally
{
App.Current.Dispatcher.Invoke(() =>
{
IsDialogLoadingOpen = false;
});
}
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private bool m_isDialogLoadingOpen;
public bool IsDialogLoadingOpen
{
get => m_isDialogLoadingOpen;
set => SetProperty(ref m_isDialogLoadingOpen, value);
}
private bool m_isDialogLogOpen;
public bool IsDialogLogOpen
{
get => m_isDialogLogOpen;
set => SetProperty(ref m_isDialogLogOpen, value);
}
}
当我直接绑定到IOPortsView中的ObservableCollection DataPorts时,它为什么有效:<DataGrid Name="dataGrid" ItemsSource="{Binding DataPorts}">
?所以我只能在提供的代码中更改它,然后它就可以工作了。只有当我绑定到ICollectionView CollectionDataPort时,它才不起作用。
为什么会发生这种情况?这种在WPF应用程序中使用线程的方式有缺陷吗?任何帮助将不胜感激。
1条答案
按热度按时间elcex8rz1#
发生您遇到的错误是因为ICollectionView示例CollectionDataPorts绑定到UI,因此必须在UI线程上修改。在您的代码中,您试图从单独的工作线程修改CollectionDataPorts,这是不允许的,并且会导致您提到的NotSupportedException。
若要解决此问题,应确保在UI线程上执行对CollectionDataPorts的修改。您可以使用Dispatcher在正确的线程上调用修改。
我将DataPorts集合的初始化移到了IOPortsViewModel的构造函数中。然后,在创建newProcessedLeftMenuTree时,我访问IOPortsViewModel示例并更新UI线程上的CollectionDataPorts属性。
通过在UI线程上执行修改,可以避免NotSupportedException并确保ICollectionView得到正确更新。