Winforms BackgroundWorker组件的2个问题

Winforms BackgroundWorker组件的2个问题,winforms,datagridview,backgroundworker,Winforms,Datagridview,Backgroundworker,首先,我知道我应该使用适当的线程技术(Threadpool、BeginInvoke等)来实现这一点,但这一点我现在有点不知所措,需要一段时间来阅读材料并理解它(如果您对我的场景有任何URL引用,请随时发布) 在此期间,我使用backgroundWorker提取一个非常大的结果集,并用它填充DatagridView。我成功地在我的DoWork事件中创建了一个SortableBindingList,并将其传递到结果中。在RunWorkerCompleted事件中,我将该SortableBinding

首先,我知道我应该使用适当的线程技术(Threadpool、BeginInvoke等)来实现这一点,但这一点我现在有点不知所措,需要一段时间来阅读材料并理解它(如果您对我的场景有任何URL引用,请随时发布)

在此期间,我使用backgroundWorker提取一个非常大的结果集,并用它填充DatagridView。我成功地在我的DoWork事件中创建了一个
SortableBindingList
,并将其传递到结果中。在
RunWorkerCompleted
事件中,我将该
SortableBindingList
强制转换并绑定到我的网格。我关注的两个主要领域如下:

1)访问私有变量。 我想将两个参数
List
中的一个传递到我的
DoWork
事件中,但根据传递给它的列表运行不同的查询。我可以通过声明一个充当排序标志的类级私有布尔变量来解决这个问题。这样问似乎很愚蠢,但在我的
DoWork
中,是否允许我访问该私有变量并相应地路由查询?(我已经测试过了,它确实有效,没有出现任何错误)


很抱歉,我的问题很长,但我将非常感谢您的回复!谢谢大家!

您的代码似乎做了正确的事情

至于UI线程更新屏幕所需的8秒时间,您对此无能为力。看看我对你的回答

要优化UI部分,您可以尝试在网格或其包含面板上调用and

您还可以考虑减少数据绑定期间的处理量。例如:

  • 在网格中完成的计算可以移动到数据模型中(从而在工作线程中完成)
  • 如果网格根据数据模型自动计算其列,则尝试对它们进行硬编码
  • 编辑:对业务层中的数据进行分页,使网格一次只显示少量行

我认为解决您问题的最简单方法是在
DoWork
中设置网格的数据源,而不是使用您提到的
Dispatcher.BeginInvoke
。大概是这样的:

private bool SearchEngaged = false;

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    SortableBindingList<Task> sblResult = GetTasks((List<long>)e.Argument, worker, e);

    BeginInvoke((Action<object>)(o => dataGridView1.DataSource = o), sblResult);
}

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled) {
        lblStatus.Text = "Operation was cancelled";
    }
    else if (e.Error != null) {
        lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
    }
    else
    {
        dgv.Enabled = true;
        TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
        lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
        lblDuration.Visible = true;
        lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
        cmdAsyncCancel.Visible = false;
        tmrProgressUpdate.Stop();
        tmrProgressUpdate.Enabled = false;
        pbStatus.Visible = false;
    }
}
private bool SearchEngaged=false;
私有void bgw_DoWork(对象发送方,DoWorkEventArgs e)
{
BackgroundWorker worker=发件人作为BackgroundWorker;
SortableBindingList sblResult=GetTasks((List)e.Argument,worker,e);
BeginInvoke((操作)(o=>dataGridView1.DataSource=o),sblResult);
}
私有void bgw_RunWorkerCompleted(对象发送方,RunWorkerCompletedEventArgs e)
{
如果(如已取消){
lblStatus.Text=“操作已取消”;
}
否则如果(例如错误!=null){
lblStatus.Text=string.Format(“错误:{0}”,e.Error.Message);
}
其他的
{
dgv.Enabled=true;
TimeSpan Duration=DateTime.Now.TimeOfDay-DurationStart;
lblStatus.Text=string.Format(“显示{0}{1}”,sblResult.Count,“任务”);
lblDuration.Visible=true;
lblDuration.Text=string.Format(“(以{0}秒检索的数据)”,Math.Round(Duration.TotalSeconds,2));
cmdAsyncCancel.Visible=false;
tmrProgressUpdate.Stop();
tmrProgressUpdate.Enabled=false;
pbStatus.Visible=false;
}
}
至于私有变量问题,我认为在你的情况下不会有任何问题。如果您使用某个UI事件对其进行更改,只需将私有字段标记为
volatile
volatile
关键字的文档可在此处找到:


Lambda表达式…很好!谢谢你的回复,约格什。虽然当我在BeginInvoke之后放置断点并运行应用程序时,“sblResult”显示为null。有什么想法吗?“Dispatcher.BeginInvoke”应该重写为“Dispatcher.CurrentDispatcher.BeginInvoke”(WindowsBase.dll)网格根本不会被填充。我以前弄错了-我看到sblResult确实包含数据,但网格中什么也没有发生。我有什么遗漏吗?对不起,我发布了一个WPF相关的解决方案。再次查看我的帖子。编辑,嘿,约格什。你在我的第一个问题上帮了我很多忙,但是我担心上面提到的并不能解决我的第二个问题。这一次,我的整个UI都被一个waitcursor锁定了,而我在Hey christian之前没有得到这个!谢谢你的意见!调用SuspendLayout和ResumeLayout并不能完全解决我的问题。没有任何这样的计算-更多的单元格格式。我唯一能做的就是在网格的CellFormatting事件中。我正在考虑两种解决方法。一:在DoWork中按照正常方式运行查询,对于列表中返回的每个对象,我可以创建并填充DataGridViewRow,并将其传递到ProgressChanged事件中,然后我可以将该行添加到网格中(不知道这是否值得额外处理,甚至可能!)第二:我知道这违背了所有关于winforms网格的最佳实践,但在我的情况下,我认为扩展网格以方便分页可能是值得的。Iv注意到,如果我只带回100行,网格将在0.4秒内“绘制”自身。你的想法?Re:一个——不是个好主意。您将在一个线程中填充一个与UI相关的大型可变对象,然后将其传递给另一个线程进行渲染。这是一个多线程的“代码味道”,我不推荐使用它。如果你在BL中进行分页,你必须自己完成所有事情。如果用户更改了排序顺序,则必须返回BL,使用新的排序顺序重新查询数据,然后重新获取新数据的第一页。
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
    if (e.Cancelled) {
        lblStatus.Text = "Operation was cancelled";
    }
    else if (e.Error != null) {
        lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
    }
    else {
        SortableBindingList<Task> sblResult = (SortableBindingList<Task>)e.Result;
        dgv.DataSource = sblResult;
        dgv.Enabled = true;
        TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
        lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
        lblDuration.Visible = true;
        lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
        cmdAsyncCancel.Visible = false;
        tmrProgressUpdate.Stop();
        tmrProgressUpdate.Enabled = false;
        pbStatus.Visible = false;
    }
}
private bool SearchEngaged = false;

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    SortableBindingList<Task> sblResult = GetTasks((List<long>)e.Argument, worker, e);

    BeginInvoke((Action<object>)(o => dataGridView1.DataSource = o), sblResult);
}

private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled) {
        lblStatus.Text = "Operation was cancelled";
    }
    else if (e.Error != null) {
        lblStatus.Text = string.Format("Error: {0}", e.Error.Message);
    }
    else
    {
        dgv.Enabled = true;
        TimeSpan Duration = DateTime.Now.TimeOfDay - DurationStart;
        lblStatus.Text = string.Format("Displaying {0} {1}", sblResult.Count, "Tasks");
        lblDuration.Visible = true;
        lblDuration.Text = string.Format("(data retrieved in {0} seconds)", Math.Round(Duration.TotalSeconds, 2));
        cmdAsyncCancel.Visible = false;
        tmrProgressUpdate.Stop();
        tmrProgressUpdate.Enabled = false;
        pbStatus.Visible = false;
    }
}