C# 在 WPF 中,Windows 窗体中的 Suspend/ResumeLayout() 和 BackgroundWorker() 等价物是什么
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/82847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In WPF, what is the equivalent of Suspend/ResumeLayout() and BackgroundWorker() from Windows Forms
提问by mrbradleyt
If I am in a function in the code behind, and I want to implement displaying a "Loading..." in the status bar the following makes sense, but as we know from WinForms is a NoNo:
如果我在后面的代码中的一个函数中,并且我想实现在状态栏中显示“正在加载...”,以下是有意义的,但正如我们从 WinForms 知道的那样:
StatusBarMessageText.Text = "Loading Configuration Settings...";
LoadSettingsGridData();
StatusBarMessageText.Text = "Done";
What we all now from WinForms Chapter 1 class 101, is the form won't display changes to the user until after the Entire Function completes... meaning the "Loading" message will never be displayed to the user. The following code is needed.
我们现在从 WinForms 第 1 章第 101 章学到的是,在整个函数完成之前,表单不会向用户显示更改……这意味着永远不会向用户显示“正在加载”消息。需要以下代码。
Form1.SuspendLayout();
StatusBarMessageText.Text = "Loading Configuration Settings...";
Form1.ResumeLayout();
LoadSettingsGridData();
Form1.SuspendLayout();
StatusBarMessageText.Text = "Done";
Form1.ResumeLayout();
What is the best practice for dealing with this fundamental issue in WPF?
在 WPF 中处理这个基本问题的最佳实践是什么?
回答by Abe Heidebrecht
The easiest way to get this to work is to add the LoadSettingsGridData to the dispatcher queue. If you set the operation's DispatcherPriority sufficiently low enough, the layout operations will occur, and you will be good to go.
使其工作的最简单方法是将 LoadSettingsGridData 添加到调度程序队列。如果您将操作的 DispatcherPriority 设置得足够低,布局操作就会发生,您就可以开始了。
StatusBarMessageText.Text = "Loading Configuration Settings...";
this.Dispatcher.BeginInvoke(new Action(LoadSettingsGridData), DispatcherPriority.Render);
this.Dispatcher.BeginInvoke(new Action(() => StatusBarMessageText.Text = "Done"), DispatcherPriority.Render);
回答by mrbradleyt
In reading the article by Shawn Wildermuth WPF Threads: Build More Responsive Apps With The Dispatcher.
在阅读 Shawn Wildermuth 的文章WPF 线程:使用 Dispatcher 构建更具响应性的应用程序。
I came accross the following, which states you can use the Background Worker just like you could in WindowsForms. Fancy that:
我遇到了以下内容,其中指出您可以像在 WindowsForms 中一样使用后台工作器。幻想:
BackgroundWorker Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker. Figure 7 shows typical usage of the BackgroundWorker class.
Figure 7 Using a BackgroundWorker in WPF
BackgroundWorker _backgroundWorker = new BackgroundWorker(); ... // Set up the Background Worker Events _backgroundWorker.DoWork += _backgroundWorker_DoWork; backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted; // Run the Background Worker _backgroundWorker.RunWorkerAsync(5000); ... // Worker Method void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // Do something } // Completed Method void _backgroundWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { statusText.Text = "Cancelled"; } else if (e.Error != null) { statusText.Text = "Exception Thrown"; } else { statusText.Text = "Completed"; } }
The BackgroundWorker component works well with WPF because underneath the covers it uses the AsyncOperationManager class, which in turn uses the SynchronizationContext class to deal with synchronization. In Windows Forms, the AsyncOperationManager hands off a WindowsFormsSynchronizationContext class that derives from the SynchronizationContext class. Likewise, in ASP.NET it works with a different derivation of SynchronizationContext called AspNetSynchronizationContext. These SynchronizationContext-derived classes know how to handle the cross-thread synchronization of method invocation.
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects.
BackgroundWorker 既然您已经了解了 Dispatcher 的工作原理,您可能会惊讶地发现在大多数情况下您找不到它的用处。在 Windows Forms 2.0 中,Microsoft 引入了一个用于非 UI 线程处理的类,以简化用户界面开发人员的开发模型。这个类称为BackgroundWorker。图 7 显示了 BackgroundWorker 类的典型用法。
图 7 在 WPF 中使用 BackgroundWorker
BackgroundWorker _backgroundWorker = new BackgroundWorker(); ... // Set up the Background Worker Events _backgroundWorker.DoWork += _backgroundWorker_DoWork; backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted; // Run the Background Worker _backgroundWorker.RunWorkerAsync(5000); ... // Worker Method void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { // Do something } // Completed Method void _backgroundWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) { statusText.Text = "Cancelled"; } else if (e.Error != null) { statusText.Text = "Exception Thrown"; } else { statusText.Text = "Completed"; } }
BackgroundWorker 组件与 WPF 配合得很好,因为在幕后它使用 AsyncOperationManager 类,而后者又使用 SynchronizationContext 类来处理同步。在 Windows 窗体中,AsyncOperationManager 传递从 SynchronizationContext 类派生的 WindowsFormsSynchronizationContext 类。同样,在 ASP.NET 中,它使用 SynchronizationContext 的不同派生,称为 AspNetSynchronizationContext。这些 SynchronizationContext 派生类知道如何处理方法调用的跨线程同步。
在 WPF 中,此模型使用 DispatcherSynchronizationContext 类进行扩展。通过使用 BackgroundWorker,可以自动使用 Dispatcher 来调用跨线程方法调用。好消息是,由于您可能已经熟悉这种常见模式,因此您可以在新的 WPF 项目中继续使用 BackgroundWorker。
回答by Ernest
Best and simplest:
最好和最简单的:
using(var d = Dispatcher.DisableProcessing())
{
/* your work... Use dispacher.begininvoke... */
}
Or
或者
IDisposable d;
try
{
d = Dispatcher.DisableProcessing();
/* your work... Use dispacher.begininvoke... */
} finally {
d.Dispose();
}