【问题标题】:Wait message while populating a datagrid control填充数据网格控件时等待消息
【发布时间】:2016-07-18 23:21:21
【问题描述】:

我有一个带有选项卡控件和多个选项卡的 C# WinForms 应用程序。其中一个选项卡包含一个数据网格控件——它只有大约 10 个元素,但数据是通过查询多个服务器来填充的,因此加载速度很慢。

当我运行我的应用程序并选择带有数据网格控件的选项卡时,应用程序似乎挂起,同时它试图查询所有服务器并填充网格。

我希望应用程序能够响应并显示“请稍候...”消息,而不是挂起,该消息将在填充数据网格后消失。

我试图做的是这样创建一个后台工作人员:

if (tabctrl.SelectedTab == tabctrl.TabPages["tabServices"])
{

    this.dgrdServices.RowPrePaint += new DataGridViewRowPrePaintEventHandler(dgrdServices_RowPrePaint);
    this.dgrdServices.CellContentClick += new DataGridViewCellEventHandler(dgrdServices_CellClick);

    BackgroundWorker bw = new BackgroundWorker();
    lblLoading.Visible = true;
    bw.RunWorkerAsync();
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);


}

private void bw_DoWork(object sender, DoWorkEventArgs e)
{

    PopulateServicesDataGrid();
}

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    lblLoading.Visible = false;
}

private void PopulateServicesDataGrid()
{
    int x = 0;

    foreach (Service Service in Globals.Services)
    {
        // Add a row to the datagrid for each service
        this.dgrdServices.Rows.Add();

        // Update the current service status
        Service.Status = Service.Query(Service.Server, Service.Name);
        if (Service.Status == "running")
        {
            this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.green_dot;
            this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.stop_enabled;
        }
        else
        {
            this.dgrdServices.Rows[x].Cells[0].Value = Properties.Resources.grey_dot;
            this.dgrdServices.Rows[x].Cells[4].Value = Properties.Resources.start_enabled;
        }

        this.dgrdServices.Rows[x].Cells[1].Value = Service.Server.ToUpper();
        this.dgrdServices.Rows[x].Cells[2].Value = Service.FreindlyName;
        this.dgrdServices.Rows[x].Cells[3].Value = Service.Status;
        this.dgrdServices.Rows[x].Cells[5].Value = "Uninstall";
        this.dgrdServices.Rows[x].Cells[6].Value = Service.Name;
        x++;
    }
}

PopulateServicesDataGrid() 包含遍历某些对象并查询多个不同服务器以获取服务状态的代码。

当我尝试运行上述内容时,虽然网格没有被填充。如果我不使用后台工作人员而直接调用 PopulateServicesDataGrid 它确实可以工作(尽管应用程序挂起)。

为什么后台工作人员/数据网格填充不工作?

【问题讨论】:

  • 您必须在 e 参数中检查 RunWorkerCompleted 事件中的错误。在连接事件之前运行 RunWorkerAsync() 看起来很奇怪。不发布您的 PopulateServicesDataGrid 对我们没有帮助。
  • 后台工作进程是一个独立的进程,不会显示在网格中。对服务器的查询应该在后台工作人员中,后台工作人员返回一个数据表。然后使用返回的 DataTable 填充 DataGrid。
  • PopulateServiceDataGrid 已发布。

标签: c# winforms datagrid backgroundworker


【解决方案1】:

在您的 PopulateServicesDataGrid 中,我想您正在与一个 UI 控件进行交互,这不起作用,因为后台工作人员在与您的 UI 上下文不同的线程上运行。您需要制定一种机制以返回您想要放入网格中的信息然后返回您的 UI 线程上下文 (RunWorkerCompleted) 的方式来完成工作,使用您提出的信息填充网格工作。

每当您使用后台工作器时,您都需要分离与 UI 控件的交互,并在后台工作器完成与您的 UI 的交互后恢复。

在调用 RunWorkerAsync 之后,您还要挂接事件,请先挂接您的事件,然后再调用 RunWorkerAsync。

编辑以通过示例反映评论:

根据我看到的代码,您可以如何做到这一点的粗略示例。

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
     QueryServices()
}

private void QueryServices()
{
   foreach (Service Service in Globals.Services)
   {
       Service.Status = Service.Query(Service.Server, Service.Name);
   }
}

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        PopulateServicesDataGrid();
        lblLoading.Visible = false;
    }

    private void PopulateServicesDataGrid()
    {
        //Do everything else you are doing originally in this method minus the Service.Query calls.
    }

【讨论】:

  • 将 RunWorkerAsync 移至结束 - 谢谢!有关如何将信息返回到网格的任何建议?我之前没做过多线程
  • @Brad 看起来你已经在 Globals.Services 中有一个集合,这是一种奇怪的填充方式,但这是你必须使用的。你可以继续你的 f​​oreach 并且只对每一个进行查询调用。在完成的事件中,foreach 回到那些并执行您在方法中已有的基于 UI 的逻辑。这是假设 Service.Status 将在工作人员完成后保留其状态,鉴于当前显示的代码,我不是 100%。
  • 抱歉,您的回复我已经看了十几遍了。我不确定我是否遵循您的建议。你能详细说明或澄清吗?也许举个例子?对不起!
  • @Brad 我已经用一个例子更新了答案。它假定 Globals.Services 是一个全局变量,您可以在后台线程中运行这些查询后访问它。在 RunWorkerCompleted 中,您可以像您的逻辑一样填充您的网格,只需减去查询调用,因为这已在我介绍的 QueryServices() 方法中完成。
  • 效果很好 - 非常感谢您提供的示例。将您的答案标记为解决方案。再次感谢!
【解决方案2】:

方法 bw_DoWork 在 ThreadPool 的另一个线程中运行。从其他线程访问 WinForms 对象需要同步。最好的方法是使用 AsyncOperationManager。您应该在 GUI 线程中创建 AsyncOperation 并在 PopulateServicesDataGrid 中使用它来发送或发布结果。

另一种方式 - 通过 bw_RunWorkerComplete 中准备好的数据更新 DataGrid - 它已经由 BackgroundWorker 组件同步。

更现代的方式来做同样的事情 - 使用异步任务,但它需要基本的 TPL 知识。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多