[英]Form is displaying before async function completes
我有一个WinForms项目,该项目可以扫描给定的网络并返回有效的IP地址。 找到所有地址后,我为每个地址创建一个用户控件并将其放在表单上。 我的ping ip地址函数使用async
和Task
,我认为它们在执行其他操作之前会“等待”执行,但事实并非如此。 我的表单显示为空白,然后在5秒钟内,所有用户控件都出现在表单上。
声明:
private List<string> networkComputers = new List<string>();
这是Form_Load事件:
private async void MainForm_Load(object sender, EventArgs e)
{
//Load network computers.
await LoadNetworkComputers();
LoadWidgets();
}
LoadNetworkComputers
函数在此处:
private async Task LoadNetworkComputers()
{
try
{
if (SplashScreenManager.Default == null)
{
SplashScreenManager.ShowForm(this, typeof(LoadingForm), false, true, false);
SplashScreenManager.Default.SetWaitFormCaption("Finding computers");
}
else
Utilities.SetSplashFormText(SplashForm.SplashScreenCommand.SetLabel, "Scanning network for computers. This may take several minutes...");
networkComputers = await GetNetworkComputers();
}
catch (Exception e)
{
MessageBox.Show(e.Message + Environment.NewLine + e.InnerException);
}
finally
{
//Close "loading" window.
SplashScreenManager.CloseForm(false);
}
}
最后两个功能:
private async Task<List<string>> GetNetworkComputers()
{
networkComputers.Clear();
List<string> ipAddresses = new List<string>();
List<string> computersFound = new List<string>();
for (int i = StartIPRange; i <= EndIPRange; i++)
ipAddresses.Add(IPBase + i.ToString());
List<PingReply> replies = await PingAsync(ipAddresses);
foreach(var reply in replies)
{
if (reply.Status == IPStatus.Success)
computersFound.Add(reply.Address.ToString());
}
return computersFound;
}
private async Task<List<PingReply>> PingAsync(List<string> theListOfIPs)
{
var tasks = theListOfIPs.Select(ip => new Ping().SendPingAsync(ip, 2000));
var results = await Task.WhenAll(tasks);
return results.ToList();
}
我真的迷住了为什么在MainForm_Load
事件中的代码完成之前显示表单。
编辑我忘了提及它在LoadNetworkComputers
加载的启动表格,该表格使用户知道该应用程序正在运行。 在这种情况下,我试图避免这种情况。 这是屏幕截图(敏感信息已被涂黑):
使用async-await的原因是使函数的调用者可以在函数必须等待某些内容时继续执行代码。
令人高兴的是,即使等待功能未完成,这也将使您的UI保持响应。 例如,如果您有一个将LoadNetworkComputers
和LoadWidgets
的按钮,您将很高兴在此相对较长的操作期间仍可以重新绘制窗口。
由于已将Mainform_Load
定义为异步,因此您已表示希望UI继续而不等待LoadNetWorkComputers的结果。
在对Eric Lippert的采访中 (在中间搜索async-await),async-await与做晚餐的厨师进行了比较。 每当厨师发现他必须等待面包烘烤时,他就会开始四处看看是否可以做其他事情,然后开始做。 烤了一段时间后,他继续准备烤面包。
通过保持表单加载异步,您的表单可以显示自己,甚至显示正在加载网络计算机的指示。
更好的方法是创建一个简单的启动对话框,通知操作员程序正在忙于加载网络计算机。 启动对话框的异步表单加载可以执行操作,并在完成后关闭表单。
public class MyStartupForm
{
public List<string> LoadedNetworkComputers {get; private set;}
private async OnFormLoad()
{
// start doing the things async.
// keep the UI responsive so it can inform the operator
var taskLoadComputers = LoadNetworkComputers();
var taskLoadWidgets = LoadWidgets();
// while loading the Computers and Widgets: inform the operator
// what the program is doing:
this.InformOperator();
// Now I have nothing to do, so let's await for both tasks to complete
await Task.WhenAll(new Task[] {taskLoadComputers, taskLoadWidgets});
// remember the result of loading the network computers:
this.LoadedNetworkComputers = taskLoadComputers.Result;
// Close myself; my creator will continue:
this.Close();
}
}
和你的主要形式:
private void MainForm_Load(object sender, EventArgs e)
{
// show the startup form to load the network computers and the widgets
// while loading the operator is informed
// the form closes itself when done
using (var form = new MyStartupForm())
{
form.ShowDialog(this);
// fetch the loadedNetworkComputers from the form
var loadedNetworkComputers = form.LoadedNetworkComputers;
this.Process(loadedNetworkComputers);
}
}
现在,在加载时,将在加载项目时显示StartupForm而不是您的主窗体。.会通知操作员为何尚未显示主窗体。 加载完成后,StartupForm将自行关闭,并继续加载主窗体
我的表单显示为空白,然后在5秒钟内,所有用户控件都出现在表单上。
这是设计使然。 当UI框架要求您的应用显示表单时,它必须立即这样做。
要解决此问题,您需要确定async
工作正在进行时希望应用程序的外观,在启动时初始化为该状态,然后在async
工作完成时更新 UI。 微调框和加载页面是常见的选择。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.