查看我对这个非常微妙的主题的全面研究。如果您无法提高实际性能,您可以选择以下选项来显示等待消息:
选项#1 执行代码以与执行实际任务相同的方法同步显示等待消息。只需将这一行放在冗长的过程之前:
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => { /* Your code to display a waiting message */ }));
它将在 Invoke() 结束时处理主调度程序线程上的待处理消息。
注意: 选择 Application.Current.Dispatcher 但 Dispatcher.CurrentDispatcher 的原因在 here 解释。
选项 #2 显示“等待”屏幕并更新 UI(处理待处理的消息)。
为此,WinForms 开发人员执行了Application.DoEvents 方法。 WPF 提供了两种替代方案来实现类似的结果:
选项 #2.1 使用 DispatcherFrame class。
检查来自MSDN 的有点笨重的示例:
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), frame);
Dispatcher.PushFrame(frame);
}
public object ExitFrame(object f)
{
((DispatcherFrame)f).Continue = false;
return null;
}
选项 #2.2 调用空操作
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, (Action)(() => { }));
查看哪个(2.1 或 2.2)更好here 的讨论。恕我直言,选项 #1 仍然优于 #2。
选项 #3 在单独的窗口中显示等待消息。
当您显示的不是简单的等待消息而是动画时,它会派上用场。在我们等待另一个长渲染操作完成的同时渲染加载动画是一个问题。基本上,我们需要两个渲染线程。您不能在一个窗口中拥有多个渲染线程,但您可以将加载动画放在一个具有自己的渲染线程的新窗口中,并使其看起来不是一个单独的窗口。
从this github 下载 WpfLoadingOverlay.zip(这是文章“WPF 响应性:渲染期间的异步加载动画”的示例,但我找不到它在网络上)或看看下面的主要思想:
public partial class LoadingOverlayWindow : Window
{
/// <summary>
/// Launches a loading window in its own UI thread and positions it over <c>overlayedElement</c>.
/// </summary>
/// <param name="overlayedElement"> An element for overlaying by the waiting form/message </param>
/// <returns> A reference to the created window </returns>
public static LoadingOverlayWindow CreateAsync(FrameworkElement overlayedElement)
{
// Get the coordinates where the loading overlay should be shown
var locationFromScreen = overlayedElement.PointToScreen(new Point(0, 0));
// Launch window in its own thread with a specific size and position
var windowThread = new Thread(() =>
{
var window = new LoadingOverlayWindow
{
Left = locationFromScreen.X,
Top = locationFromScreen.Y,
Width = overlayedElement.ActualWidth,
Height = overlayedElement.ActualHeight
};
window.Show();
window.Closed += window.OnWindowClosed;
Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.Start();
// Wait until the new thread has created the window
while (windowLauncher.Window == null) {}
// The window has been created, so return a reference to it
return windowLauncher.Window;
}
public LoadingOverlayWindow()
{
InitializeComponent();
}
private void OnWindowClosed(object sender, EventArgs args)
{
Dispatcher.InvokeShutdown();
}
}