停止 WPF 中的后台辅助角色

本文关键字:角色 后台 WPF 停止 | 更新日期: 2023-09-27 18:31:07

我有WPF应用程序。

在页面上,我有诸如"繁忙指示器"之类的控件,当PanelLoading true时显示

<Grid Panel.ZIndex="1000" HorizontalAlignment="Stretch" Grid.RowSpan="3" Visibility="{Binding PanelLoading, Converter={StaticResource BoolToVis}}">
        <controls:LoadingPanel x:Name="loadingPanel" VerticalAlignment="Stretch"
                    IsLoading="{Binding PanelLoading}"
                    Message="Wait"
                    SubMessage="a minute"
                               />
    </Grid>

在此页面上,我还有其他控件(文本框,组合框等)

到项目源,我绑定了一些值,例如:

public string SearchWord
    {
        get { return _searchWord; }
        set
        {
            _searchWord = value;
            OnPropertyChanged("SearchWord");
            RunFilterWorker();
        }
    }

在上面的示例中RunFilterWorker每次在文本框中更改文本时都会尝试运行。

RunFilterWorker是一种创建 BW 并运行它的方法。

private void RunFilterWorker()
    {
        if (_filterWorker == null)
        {
            _filterWorker = CreateBackgroundWorker();//create if null (only on start)
        }
        else
        {
            if(_filterWorker.IsBusy)return; //do nothing if busy
        }
        _filterWorker.RunWorkerAsync();
    }
        private BackgroundWorker CreateBackgroundWorker()
    {
        var bw = new BackgroundWorker();
        bw.WorkerSupportsCancellation = true;
        bw.DoWork += filterWorker_DoWork;
        bw.RunWorkerCompleted += filterWorker_CompleteWork;
        return bw;
    }

对于这个 BW,我将处理程序绑定DoWorkRunWorkerCompleted

        private async void filterWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        PanelLoading = true;
        var products = await FilterProducts();
        e.Result = products;
    }
    private void filterWorker_CompleteWork(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            if (e.Cancelled) return;
            var products = (e.Result) as List<ProductDTO>;
            Products = products;
            PanelLoading = false;
        }
        else
        {
            LogHelper.Log(e.Error);
        }
    }

因此,当 BW 开始工作时 - PanelLoading 设置为 true 并显示 BusyIndicator。但是Complete指标什么也没发生,它仍然在旋转。我敢肯定这不是指标控制的问题,而是 BW 的问题。


要@Servy:

private void filterWorker_CompleteWork(object sender, RunWorkerCompletedEventArgs e)
    {
        PanelLoading = false;
        if (e.Error == null)
        {
            if (e.Cancelled) return;
            var products = (e.Result) as List<ProductDTO>;
            Products = products;
        }
        else
        {
            LogHelper.Log(e.Error);
        }
    }

没有帮助,不会隐藏指示器。

停止 WPF 中的后台辅助角色

只有在 BGW 没有错误且未取消的情况下,您才能到达PanelLoading = false;行。 但这不是你想要的行为。 您希望加载面板消失,而不管 BGW 停止的原因如何,因此您的程序逻辑需要反映这一点。

您需要做的就是将该行移动到已完成处理程序的开头。


您还有另一个不相关的问题。 您正在将async与BackgroundWorker混合和匹配,这真的不应该。 BGW 希望DoWork处理程序是同步方法。 它会在完成执行处理程序后立即将自己标记为已完成,但您正在做的是让您的处理程序除了开始一些工作之外什么都不做,并且直到处理程序结束后很长时间才真正完成这项工作。 因此,在 BGW 完成之前,您不会实际设置结果。

由于您已经有一个Task返回异步方法,并且您可以使用async,因此完全废弃BGW即可。 它在这里一事无成。 制作RunFilterWorker async,让它处理抛出的任何错误,让它显示和隐藏加载面板等。

BGW是一种非常过时的技术,正如Servy正确指出的那样,它根本不理解async。给它一个async DoWork只是自找麻烦(特别是,BGW在你期望之前就完成了)。

我有一个博客系列,展示了Task.Run如何优于 BGW,但在这种情况下,我会同意 Servy 的观点,您可能不需要 BGW Task.Run .一个简单的Task就足够了:

Task _filterWorker;
private void RunFilterWorker()
{
  if (_filterWorker == null)
  {
    _filterWorker = FilterAsync();
  }
}
private Task FilterAsync()
{
  PanelLoading = true;
  try
  {
    Products = await FilterProducts();
  }
  catch (Exception ex)
  {
    LogHelper.Log(ex);
  }
  PanelLoading = false;
  // Ensure the calling method has set _filterWorker before we clear it.
  await Task.Yield();
  _filterWorker = null;
}