在 UI 线程上下文中执行代码的正确方法?

the proper way to execute code in the UI Thread context?

我想从另一个线程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 : 我不知道如何实现这样的东西

还有其他方法吗?哪个是首选方式?

注意: 调用任务的线程不一样UI线程所以Progress可能在这里不起作用

你把它弄得太复杂了,IMO:

F.Invoke((MethodInvoker)delegate { F.ls1.Items.Add("NewItem"); });

会很好用。你需要这里的async吗?我不认为你会这样做,因为这不太可能是一项昂贵的操作:为此释放工作线程并没有太大好处。

注意:我个人更喜欢 F 直接封装更多内容,请注意;例如:

F.Invoke((MethodInvoker)delegate { F.AddCategory("NewItem"); });

我总是建议开发人员避免 Invoke/BeginInvoke/RunAsync,因为它会助长糟糕的设计:您的 backend/business 逻辑类型最终会驱动 UI(并依赖于特定的 UI 框架)。

更好的方法是使用 IProgress<T>/Progress<T>(如果您的 UI 更新是进度更新)或 IObservable<T>(如果您的后端异步生成一系列值)。

向列表添加项目对我来说更像是一个异步系列,但由于您特别询问了 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 层然后消耗这些进度 reports/values 并相应地更新 UI。