表单在异步功能完成之前显示

时间:2016-11-17 19:29:10

标签: c# multithreading thread-safety async-await task

我有一个WinForms项目,它扫描给定的网络并返回有效的IP地址。找到所有地址后,我为每个地址创建一个用户控件并将其放在表单上。我的ping ip地址的功能使用asyncTask我认为会等待"在做其他事之前执行,但事实并非如此。我的表单显示为空白,然后在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中添加一个启动表单,让用户知道应用程序正在运行。当表格出现在那之后,我试图避免。这是一个屏幕截图(敏感信息已被涂黑): enter image description here

2 个答案:

答案 0 :(得分:2)

使用async-await的原因是,只要你的函数必须等待某事,函数的调用者就可以继续执行代码。

好的一点是,即使等待的功能没有完成,这也会让你的UI保持响应。例如,如果你有一个LoadNetworkComputersLoadWidgets的按钮,你会很高兴在这个相对较长的行动中你的窗口仍然会重新粉刷。

由于您已将Mainform_Load定义为异步,因此您已表示希望在不等待LoadNetWorkComputers结果的情况下继续使用UI。

In this interview with 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自行关闭并继续加载主窗体

答案 1 :(得分:1)

  

我的表单显示为空白,然后在5秒内,所有用户控件都显示在表单上。

这是设计的。当UI框架要求您的应用显示表单时,必须立即执行

要解决此问题,您需要在async工作进行时确定您的想要您的应用的样子,在启动时初始化为该状态,并且然后在async工作完成时更新用户界面。旋转器和加载页面是一种常见的选择。