是否意味着必须在每个UI控件上调用Invoke()方法?

时间:2014-07-01 12:38:53

标签: c# multithreading user-interface backgroundworker

以下是将Insert放入数据库表的方法。我在BackGroundWorker线程的DoWork()内调用此方法。它显然抛出了“交叉线程操作无效...”错误。据我了解,如果要在Invoke()内访问,我可以在UI控件上使用DoWork()方法。是否意味着以下每个UI控件都必须调用?有没有更好的方法来实现这一目标?

    private void AddToOccupations()
    {
        if (dataGridViewSearch.SelectedRows.Count > 0)
        {
            foreach (DataGridViewRow datarow in dataGridViewSearch.SelectedRows)
            {
                try
                {
                    AllocationModel occupation = new AllocationModel()
                    {
                        User_ID = userID,
                        Merge_Status = (int)((MergeStatus)Enum.Parse(typeof(MergeStatus), cmbMergeStatus.SelectedItem.ToString())),
                        Start_Date = dateTimePickerFROMDate.Value,
                        Seat_Type = datarow.Cells[2].Value.ToString(),
                        Occupation_Status = cmbStatus_Type.SelectedItem.ToString(),
                        Session = datarow.Cells[3].Value.ToString(),
                        Seat_Number = (Int32)datarow.Cells[0].Value,
                        Number_of_Guests = (Int32)datarow.Cells[1].Value
                    };

                    // Call the service method 
                    var success = this.allocationService.AddToOccupation(occupation);

                    if (success)
                    {
                        // display the message box
                        MessageBox.Show(
                            Resources.Add_Occupation_Success_Message,
                            Resources.Add_Occupation_Success_Title,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);

                        // Reset the screen
                        //this.Reset();

                        DoCurrentFreeSearch();
                    }
                    else
                        MessageBox.Show("No Records Inserted!");
                }
                catch (FormatException ex)
                {
                    errorMessage = "Insert Error: \n";
                    errorMessage += ex.Message;
                    MessageBox.Show(errorMessage);
                }
            }
        }
        else
            MessageBox.Show("Warning! No Row is selected...");
    }

3 个答案:

答案 0 :(得分:2)

您应该将工作人员代码与GUI代码分开。你在这个数据网格上有DataSource吗?您基本上应该首先从网格中获取所需的数据(所选行),然后从GUI代码(按钮单击或其他)将它们传递给后台工作程序。然后,您可以通过BackgroundWorker.ReportProgress(在GUI线程上执行progress事件)报告工作进度。

如果您无法完全将GUI代码与工作人员代码分离,那么您必须使用Invoke来完成其余工作。但是,从您的代码中不清楚为什么需要它,ReportProgress应该足够了。

答案 1 :(得分:1)

标题问题的一般答案是:是的。

如果后台工作线程需要访问UI项目,则只能通过Invoking方法来实现。

通过以解耦方式访问其数据源,可以最好地解决将数据从BW放入DGV的问题。我刚做了一点测试,看看我原来的帖子中的建议是否有效,看起来很好。

因此,与Bitmap一样,您可以创建两个DataSources作为属性;一个用于填充后台工作者,另一个用作DGV的Datasource

public List<SeatData> theSeats_buffer { get; set; }
public List<SeatData> theSeats_DS { get; set; }

在BW线程DoWork()中,通过调用适当的函数void或bool getSeatData()填充theSeats_buffer列表,当您完成工作负载后,将数据传递到theSeats_DS,也许是这样的:

theSeats_DS= new List<Cols>(); theSeats_DS.AddRange(theSeats_buffer);

同样,必须通过锁定接收列表theSeats_DS来使此操作成为线程安全的。

由于DataSource已重新创建,因此应在bw_RunWorkerCompleted事件中重新分配;我做了正确的同时使显示面板1无效:

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if ((e.Cancelled == true))
    { this.tbProgress.Text += "Cancelled!";    }
    else if (!(e.Error == null))
    {  this.tbProgress.Text += ("Error: " + e.Error.Message);    }
    else
    { panel1.Invalidate(); DGV.DataSource = theSeats_DS; }
}

关于DB Inserts,我不知道它是如何相关的。这个答案只是关于从某个地方异步获取数据并将它们发送到用户界面。

将数据从DGV获取到数据库中并不会在BW线程中发生,但至少不会在传入更改时触发。如果你使用DGV输入,并发将是一个问题!这是非常糟糕的,如果一个人试图保留的座位实际上是按你输入的时间。但这无法避免。但是,您需要确保传入的更改不会消除输入.OTOH,信号会很好..

并发很棘手!

答案 2 :(得分:-1)

由于控件在UI线程上运行,而不在后台工作线程上运行,因此需要调用它们的所有函数。