C# 在调度程序上运行异步操作赢得';完成或冻结用户界面
我只想运行Drop action asynchronous,在移动大量数据时显示一个繁忙的对话框。因为源集合只能由C# 在调度程序上运行异步操作赢得';完成或冻结用户界面,c#,wpf,asynchronous,mvvm,drag-and-drop,C#,Wpf,Asynchronous,Mvvm,Drag And Drop,我只想运行Drop action asynchronous,在移动大量数据时显示一个繁忙的对话框。因为源集合只能由调度程序访问,所以我需要调用它 这样,等待的调用将永远不会完成/对话框将永远不会关闭。 在此处使用Invoke而不是InvokeAsync将导致NotSupportedException,原因与上述相同 public async void Drop(IDropInfo dropInfo) { MainViewModel.Current.ShowBusyDialog();
调度程序访问,所以我需要调用它
这样,等待的调用将永远不会完成/对话框将永远不会关闭。
在此处使用Invoke
而不是InvokeAsync
将导致NotSupportedException
,原因与上述相同
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
await Task.Run(() =>
{
// This would crash:
// Dispatcher.CurrentDispatcher.Invoke(() =>
await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
var data = dropInfo.Data as SomeObject;
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (data != null)
{
// Operate with collection
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
// Operate with collection
}
});
});
// Never reaches this point
MainViewModel.Current.CloseDialog();
}
更新--------------------------------------------------------------------
创建并上载一个示例以显示问题:您正在UI dispatcher上运行所有这些代码
await Dispatcher.CurrentDispatcher.InvokeAsync(() =>
你需要做的是
在此处运行所有工作等待任务。运行(()=>
完成后,使用dispatcher更新UI
顺便说一句,如果您要更新实现INotifyPropertyChanged的绑定属性,您甚至不必使用dispatcher。从任何线程更新属性。绑定将自动将更新打包到UI线程
您在代码中使用的Dispatcher是UI Dispatcher。这意味着,当您向其分派方法时,您正在UI线程上执行该方法。仅在最后可能的时刻使用Dispatcher,并且仅在
- 从其他线程更新UI
- 在应用程序生命周期事件之后安排UI工作(请参阅DispatcherPriority枚举)
还有一个问题
Dispatcher.CurrentDispatcher
检索…当前Dispatcher。什么是当前Dispatcher?它是当前线程的Dispatcher。在第一个示例中,当前线程是后台线程。您希望从UI使用Dispatcher。这是第一个示例,但经过调整
public async void Drop(IDropInfo dropInfo)
{
// We are in the UI thread here!
MainViewModel.Current.ShowBusyDialog();
// as we are on the UI thread, no worries touching the collection here
var collection = (ObservableCollection<SomeObject>)
((ICollectionView)dropInfo.TargetCollection).SourceCollection;
// as we are on the UI thread, this is the UI dispatcher.
var dispatcher = Dispatcher.CurrentDispatcher;
await Task.Run(() =>
{
// We are on a background thread here!
var data = dropInfo.Data as SomeObject;
YourResults results = null;
if (data != null)
{
results = WhateverProcessingTakesALongTime();
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
results = SameHereIHaveNoIdea(dropInfo.Data);
}
// Now, let's update the UI on the UI thread.
await dispatcher.InvokeAsync(() =>
{
UpdateStuffWithStuff(collection, results);
});
});
MainViewModel.Current.CloseDialog();
}
public异步void Drop(IDropInfo-dropInfo)
{
//我们在这里的UI线程中!
MainViewModel.Current.ShowBusyDialog();
//因为我们是在UI线程上,所以不必担心这里的集合
变量集合=(可观测集合)
((ICollectionView)dropInfo.TargetCollection).SourceCollection;
//由于我们在UI线程上,这是UI调度程序。
var dispatcher=dispatcher.CurrentDispatcher;
等待任务。运行(()=>
{
//我们在这里的背景线程!
var data=dropInfo.data作为SomeObject;
YourResults=null;
如果(数据!=null)
{
结果=WhateVerProcessingTakesOngTime();
}
else if(dropInfo.Data是IEnumerable)
{
结果=SameHereIHaveNoIdea(dropInfo.Data);
}
//现在,让我们在UI线程上更新UI。
等待dispatcher.InvokeAsync(()=>
{
UpdateStuffWithStuff(收集、结果);
});
});
MainViewModel.Current.CloseDialog();
}
我遗漏了什么,或者我如何才能让它像预期的那样工作
正如其他人所指出的,Task.Run
将在后台线程上执行工作,Dispatcher.InvokeAsync
将在UI线程上执行工作。因此,除了UI线程之外,您的代码实际上没有在任何地方执行任何实际工作,这就是它“阻塞”的原因
最干净的解决方案是在UI线程上从UI对象复制所有必要的信息,在线程池上执行任何后台工作,最后(如果需要)将任何结果复制回UI对象
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
// First, copy the data out of the UI objects.
List<SomeObject> list;
var data = dropInfo.Data as SomeObject;
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (collection != null)
{
list = collection.ToList();
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
list = ((IEnumerable<SomeObject>)dropInfo.Data).ToList();
}
// Then do the background work.
await Task.Run(() =>
{
// Operate with `list`
});
// Finally, update the UI objects after the work is complete.
MainViewModel.Current.CloseDialog();
}
public异步void Drop(IDropInfo-dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
//首先,从UI对象中复制数据。
名单;
var data=dropInfo.data作为SomeObject;
变量集合=(可观测集合)
((ICollectionView)dropInfo.TargetCollection).SourceCollection;
if(集合!=null)
{
list=collection.ToList();
}
else if(dropInfo.Data是IEnumerable)
{
list=((IEnumerable)dropInfo.Data).ToList();
}
//然后做背景工作。
等待任务。运行(()=>
{
//用`列表操作`
});
//最后,在工作完成后更新UI对象。
MainViewModel.Current.CloseDialog();
}
当我在任务中运行代码时,它也会抛出一个NotSupportedException。该集合是UI上DataGrid的集合,它绑定到一个具有CollectionViewSource的对象,CollectionViewSource只是扩展了一个ObservableCollection。因此UI(DataGrid)要更新集合属性,而不是我的ViewModel或绑定属性吗directly@DanielINotifyPropertyChanged属性会自动封送。很遗憾,INotifyCollectionChanged集合不会自动封送更改。您必须在本地完成任务中的工作,然后使用Dispat更新OCcher.那么,我如何不通过引用调用来完成任务中的工作呢?我正在使用Gong.DragDrop框架通过附加属性绑定Drop处理程序。您需要在后台线程上执行所有长期工作。最后,一旦您的工作完成,您将使用Dispatcher将更新应用到ObservableCollection。您的代码不会显示任何更新这是一项长期的工作,因此我无法告诉您在后台线程上执行的操作和在UI dispatcher上执行的操作之间的界限在哪里。此外,我还为答案添加了一个更新。感谢您的解释和示例代码,我用更新的代码和解释编辑了我的问题。就像标记的副本和许多其他类似的任务一样例如,对于Dispatcher.Invoke()
或InvokeAsync()
要工作,UI线程必须可以自由处理这些调用。如果您正在调用Drop()
在UI线程上,并且不从中返回,UI线程被阻塞,无法处理这些调用。@PeterDuniho我认为这根本不是重复,因为我的问题不仅仅关注调用,而是更多地关注调用
public async void Drop(IDropInfo dropInfo)
{
// We are in the UI thread here!
MainViewModel.Current.ShowBusyDialog();
// as we are on the UI thread, no worries touching the collection here
var collection = (ObservableCollection<SomeObject>)
((ICollectionView)dropInfo.TargetCollection).SourceCollection;
// as we are on the UI thread, this is the UI dispatcher.
var dispatcher = Dispatcher.CurrentDispatcher;
await Task.Run(() =>
{
// We are on a background thread here!
var data = dropInfo.Data as SomeObject;
YourResults results = null;
if (data != null)
{
results = WhateverProcessingTakesALongTime();
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
results = SameHereIHaveNoIdea(dropInfo.Data);
}
// Now, let's update the UI on the UI thread.
await dispatcher.InvokeAsync(() =>
{
UpdateStuffWithStuff(collection, results);
});
});
MainViewModel.Current.CloseDialog();
}
public async void Drop(IDropInfo dropInfo)
{
MainViewModel.Current.ShowBusyDialog();
// First, copy the data out of the UI objects.
List<SomeObject> list;
var data = dropInfo.Data as SomeObject;
var collection = (ObservableCollection<SomeObject>)
((ICollectionView) dropInfo.TargetCollection).SourceCollection;
if (collection != null)
{
list = collection.ToList();
}
else if (dropInfo.Data is IEnumerable<SomeObject>)
{
list = ((IEnumerable<SomeObject>)dropInfo.Data).ToList();
}
// Then do the background work.
await Task.Run(() =>
{
// Operate with `list`
});
// Finally, update the UI objects after the work is complete.
MainViewModel.Current.CloseDialog();
}