我想从另一个帖子更新UI
我有很多选择在UI线程上下文中执行代码:
1:使用BeginInvoke / EndInvoke方法:
理想跟踪准码
public delegate int AddItem(object Item);
public Form1 F = (Form1)Application.OpenForms["Form1"];
private static async DoSomething()
{
AddItem ad = new AddItem(F.ls1.Items.Add);
await Task.Run(() =>F.EndInvoke(F.BeginInvoke(add,"NewItem")));
}
2:使用Progress / IProgress:我不知道如何实施这样的事情
还有另外一种方法吗?哪个是首选方式?
注意:调用Task的线程不是同一个UI线程所以也许Progress不能在这里工作
答案 0 :(得分:5)
你让它变得过于复杂,IMO:
F.Invoke((MethodInvoker)delegate { F.ls1.Items.Add("NewItem"); });
会很好地运作。你需要 async
吗?我不认为你这样做,因为这不太可能是一项昂贵的操作:为此只释放工作线程并没有太多好处。
注意:如果F
直接封装了更多内容,我个人更喜欢它,请注意;例如:
F.Invoke((MethodInvoker)delegate { F.AddCategory("NewItem"); });
答案 1 :(得分:2)
我总是建议开发人员避免Invoke
/ BeginInvoke
/ RunAsync
,因为它会鼓励设计不佳:您的后端/业务逻辑类型最终会驱动用户界面(并采取依赖关系)在特定的UI框架上)。
更好的方法是使用IProgress<T>
/ Progress<T>
(如果您的UI更新是进度更新)或IObservable<T>
(如果您的后端异步生成一系列值)。< / p>
向列表中添加项目听起来更像是异步系列,但由于您特别询问IProgress<T>
我将展示一个示例:
private static async Task DoSomethingAsync()
{
Progress<object> progress = new Progress<object>(item => F.ls1.Items.Add(item));
await Task.Run(() => BackgroundWork(progress));
}
private static void BackgroundWork(IProgress<object> progress)
{
// Do background work here.
var item = "NewItem";
// Notify UI of progress.
if (progress != null)
progress.Report(item);
}
如果您想使用IObservable<T>
,那么您的代码可能如下所示:
private static void StartSomething()
{
IObservable<object> items = BackgroundWork();
items.ObserveOn(F).Subscribe(item => F.ls1.Items.Add(item), ex => HandleException(ex));
}
请注意,使用这两种方法时,执行后台工作的代码并不关心它执行的上下文。它只会生成一系列进度更新或一系列值。然后,UI层将使用这些进度报告/值并相应地更新UI。