停止 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,我将处理程序绑定DoWork
和RunWorkerCompleted
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);
}
}
没有帮助,不会隐藏指示器。
只有在 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;
}