我正在处理的应用程序基于WPF和MVVMLightToolkit。我知道我没有为你提供mcve,但整个应用程序非常复杂,很难提供这样的例子。我希望有人能够帮助全局。
在该应用程序中,一个操作需要花费大量时间(初始化内容),因此我在任务中运行它以不冻结UI:
public class MainViewModel : ViewModelBase
{
public ICommand HeavyActionCommand {get; private set;}
public MainViewModel()
{
this.HeavyActionCommand = new RelayCommand(this.HeavyAction);
}
private async void HeavyAction()
{
var subViewModel = new SubViewModel();
await Task.Run(async () => await subViewModel.ActualHeavyAction());
}
}
如果我没有将ActualHeavyAction
包裹在Task.Run
方法中,则UI会冻结。这样做,据我所知,ActualHeavyAction
不是在UI线程上运行,而是在线程池线程上运行(如果我错了,请纠正我)。
除其他事项ActualHeavyAction
初始化我需要过滤一些用户实时输入的ObservableCollection
(在以下类中,属性UserInput
绑定到{{1} })。我有类似的东西:
TextBox
直到这里,我没有任何麻烦。稍后会出现问题,当另一个操作在UI线程上运行时修改该集合。我得到了经常性的事情:
此类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection
要解决此问题,我尝试添加.NET 4.5 public class SubViewModel: ViewModelBase
{
private _userInput;
public string UserInput
{
get { return _userInput; }
set
{
if (_userInput != value)
{
_userInput = value;
this.RaisePropertyChanged();
// Run the filter on the collection when the user enters new inputs
CollectionViewSource.GetDefaultView(this.MyCollection).Refresh();
}
}
}
public ObservableCollection MyCollection {get; private set;}
public async Task ActualHeavyAction()
{
/// lots of heavy stuff
var myCollection = await _context.Objects.GetCollectionAsync();
this.MyCollection = new ObservableCollection(myCollection);
this.RaisePropertyChanged(nameof(this.MyCollection));
CollectionViewSource.GetDefaultView(this.MyCollection).Filter = MyFilter;
/// some other heavy stuff
}
public bool MyFilter(object obj)
{
// Blah blah blah
}
}
功能:
EnableCollectionSynchronization
我尝试在调用之前和之后添加它:BindingOperations.EnableCollectionSynchronization(this.MyCollection, lockObject); // lockObject is a static new object() defined in the SubViewModel class
这样做,我在修改CollectionViewSource.GetDefaultView
时没有收到异常,但在MyCollection
上调用Refresh()
并未运行CollectionView
方法(完全相同的代码)处理未在线程池线程上初始化的其他ViewModel。)
您是否知道我的代码有什么问题?
答案 0 :(得分:1)
应该在UI线程上调用function getOneSignalToken() {
window.bridge.post('onesignaltoken', {}, function(results, error){
$('#onesignal_token').html(results.token);
});
}
方法。所以你需要创建BindingOperations.EnableCollectionSynchronization
并在之前在UI线程上调用此方法,然后尝试从后台线程访问该集合。
但是ObservableCollection
中应该在后台线程上调用的唯一方法是ActualHeavyAction()
方法。
调用此方法的任务完成后,您可以创建GetCollectionAsync()
并将过滤器应用回UI线程。或者只是从任务中返回已经过滤的列表。
使用ObservableCollection
属性过滤ICollectionView
是一种灵活但非常慢的操作,因此如果您的源集合包含大量项目,这可能不是实现过滤的最佳选择。