在 WPF 的另一个对话框中运行繁重任务时运行启动对话框
Posted
技术标签:
【中文标题】在 WPF 的另一个对话框中运行繁重任务时运行启动对话框【英文标题】:Running splash dialog while running heavy task in another dialog in WPF 【发布时间】:2021-01-20 08:49:17 【问题描述】:我正在尝试执行标题中描述的任务。 HeaviWindow 执行一些繁重的任务,因此在任务完成之前不会显示。这次我想用动画加载器显示一个启动画面。
到目前为止,我使用了以下构造:
private RunHeavyTask()
ShowSplashScreen("Please wait...");
var dialog = new HeaviWindow();
dialog.ShowDialog();
private void ShowSplashScreen(string str)
Thread newWindowThread = new Thread(new ParameterizedThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start(str);
private void ThreadStartingPoint(object str)
splash = new WaitWindow((string)str);
splash.Show();
System.Windows.Threading.Dispatcher.Run();
但突然发现,在运行线程时,所有其他后台线程都停止工作。
我已尝试为此目的使用Task
:
private void ShowSplashScreen(string str)
Task.Run(() =>
Application.Current.Dispatcher.Invoke(delegate
splash = new WaitWindow((string)str);
splash.ShowDialog();
);
);
但在繁重的对话框完成任务之前不会显示启动画面。如果我使用BackgroundWorker
,结果相同。
所以我的问题 - 我如何在另一个任务中运行繁重的任务时显示启动对话框。启动对话框使用了一些动画,因此需要更新。
【问题讨论】:
But splash screen not displayed until the heavy dialog finish the task
调用ShowDialog
会阻塞执行直到它完成,这是正常的。
@Çöđěxěŕ 确切地说,问题是我怎样才能避免这种情况。
这里更重要的是你的HeaviWindow
中发生了什么,一个对话框需要在 UI 线程中显示,但是如果你的HeaviWindow
正在执行它是在 UI 线程上工作,或者它是根本不使用线程,那么您启动新线程的调用必须等待 HeaviWindow
释放 UI 线程以显示启动屏幕。
@ChrisSchaller 直到任务完成才显示 HeavyWindow,这对我来说没问题,但我想显示启动画面。当 HeavyWindow 完成任务时,它会显示出来,所以我现在关闭了启动画面。
为什么要从另一个线程启动启动画面?这不是必需的,也没有多大意义。您应该在启动工作线程之前显示该窗口。为什么这不适合你。
【参考方案1】:
试试这个:
Window splash;
private void RunHeavyTask()
ShowSplashScreen("Please wait...");
//simulate "heavy task" by sleeping for 5 seconds
Thread.Sleep(5000);
//close the splash window
splash.Dispatcher.BeginInvoke(() => splash.Close());
private void ShowSplashScreen(string str)
Thread newWindowThread = new Thread(new ThreadStart(() =>
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
//pass str to your custom Window here...
splash = new Window() Content = new ProgressBar() IsIndeterminate = true ;
splash.Closed += (s, e) =>
Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);
splash.Show();
Dispatcher.Run();
));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();
当调用RunHeavyTask()
的线程被阻塞时,它将在后台线程上显示一个带有不确定ProgressBar
的窗口。
【讨论】:
感谢@mm8 的回复。这段代码与我已经拥有的几乎相同。SetApartmentState(ApartmentState.STA)
阻塞另一个后台线程的问题,在我的情况下这是不可接受的。
@folibis:什么阻碍了什么?您的示例中也有ApartmentState.STA
。这是所有窗口线程的硬性要求。
是的,这是个问题。我有几个在后台运行的线程。在使用SetApartmentState(ApartmentState.STA)
运行启动画面线程后,所有这些线程都被阻塞了。
@folibis:请用一个最小的例子证明你的观点。
我在上面的问题中提供了代码。那就是我现在使用的代码。【参考方案2】:
不清楚究竟你想做什么。更多细节会很好,因为您的一般方法似乎需要优化。
通常,您不会在单独的线程中启动 Window
以在 UI 线程上执行繁重的工作(CPU 限制)。而是您会在后台线程上执行 CPU 密集型任务。您可以异步等待此任务,然后在任务完成后关闭启动屏幕。
以下简单示例显示了用于在后台线程上异步执行 CPU 绑定工作以保持 UI 线程响应的模式,同时使用IProgress<T>
使用注册的委托从后台线程访问 UI 线程Progress<T>
的一个实例。
该示例假定初始屏幕显示了一些进度,并为此公开了一个 Progress
属性:
// Show a splash screen,
// while executing CPU bound work asynchronously on a background thread
private async Task RunHeavyTaskAsync()
splashScreen = new Window();
splashScreen.Show();
// Use IProgress<T> to access the UI thread via a delegate,
// which is registered using the constructor of Progress<T>
var progressReporter = new Progress<double>(value => splash.Progress = value);
// Pass the IProgress<T> instance to the background thread
// and wait non-blocking (asynchronously) for the thread to complete
await Task.Run(() => DoWork(progressReporter));
// Close splash screen after 5s heavy work
splashScreen.Close();
// Will be executed on a background thread
private void DoWork(IProgress<double> progressReporter)
// Post progress from background thread (current context)
// to the UI thread (splash screen).
// IProgress<T>.Report() will execute the previously registered delegate
progressReporter.Report(50);
// Do 5s heavy work
Thread.Sleep(5000);
progressReporter.Report(100);
【讨论】:
以上是关于在 WPF 的另一个对话框中运行繁重任务时运行启动对话框的主要内容,如果未能解决你的问题,请参考以下文章
无论安装的 Lync 客户端版本如何,都从 C# WPF 桌面应用程序启动 Lync 对话