WPF 进度条工作但在 UI 线程中阻塞,即使使用异步

WPF Progress bar working but blocked in UI thread even using async

我正在尝试在我的程序中实现一个不确定的进度条。我是线程的新手,但据我所知,这里最好的选择之一是添加异步方法,并等待 "heavy" 函数执行其结果。所以我写了这个:

public void Window_Loaded(object sender, RoutedEventArgs e)
{
    firstLoad();                         
}

private async void firstLoad()
{
    LW.Title = "Loading...";
    LW.Show();

    filterTextBox.Text = defaultSearch;
    await Task.Run(() => InitializeFilter());          
}

private void InitializeFilter()
{

//Asynchronous??? 
Dispatcher.BeginInvoke(new Action(() => {

//... some lines of code that takes some time to run. 

dataGrid.ItemContainerGenerator.StatusChanged += new EventHandler(closeLoadingWindow);

}));

private void closeLoadingWindow(object sender, EventArgs e)
{
    if (LW != null)
    {
        LW.closable = true;
        LW.Close();
    }
}

firstLoad 在 window 加载时运行,显示不确定的 LW loadingWindow,运行 InitializeFilter() 方法(重的)。最后,当网格被填充和加载时,一个事件被触发,允许 LW window 被关闭并关闭它(如果我没有让它不可关闭,一个有趣的用户可以点击或使用 F4 关闭它,这不好)。

系统工作正常,在时间范围内一切都按预期进行,但加载栏被冻结,没有显示进度。相同的 LW 栏在具有类似设置的 MainWindow 中工作 我错过了什么?提前致谢!

不要使用您的调度员。 Microsoft 有先见之明,使用它的魔法 (SynchronizationContext) 能够在异步上下文中执行的方法中更新 UI 线程。这在他们的 async/await 示例中得到证明 here

而在 previous/other 情况下,您将不得不编组回主 (UI) 线程以更新 UI 线程,或者等到完成并检索结果来自共享状态的对象。由于您正在使用 async/await 那么您应该可以不使用调度程序,直接更新 UI。

as far as I know one of the best options here is to add an async method, and await the "heavy" function to perform its results

最佳选择是使用 Task.Run 将繁重的处理移至线程池,并使用 await 检索其结果。

目前的代码使用Task.Run移动到线程池,然后立即转身使用Dispatcher移动回UI线程,然后再进行繁重的操作加工。因此,它阻塞了 UI 线程。

what this particular DataGrid displays is a CollectionView, which is not thread-safe.

是的,您不能从线程池线程更新数据绑定对象。

最好的解决方案是 将繁重的处理与 UI 更新分开,像这样:

public async void Window_Loaded(object sender, RoutedEventArgs e)
{
  await firstLoadAsync();
}

private List<FilterType> InitializeFilter()
{
  //... some lines of code that takes some time to run. 
}

private async Task firstLoadAsync()
{
  LW.Title = "Loading...";
  LW.Show();

  filterTextBox.Text = defaultSearch;
  var filterData = await Task.Run(() => InitializeFilter()); // Get the plain data on a background thread
  myCollectionView = new CollectionView(filterData); // Update the UI
  if (LW != null)
  {
    LW.closable = true;
    LW.Close();
  }
}