这是否意味着必须在每个UI控件上调用Invoke()方法?
本文关键字:Invoke 调用 方法 控件 UI 意味着 是否 | 更新日期: 2023-09-27 18:10:54
下面是一个将Insert
放入数据库表的方法。我在BackGroundWorker线程的DoWork()
中调用这个方法。它显然给我抛出了"跨线程操作无效……"错误。据我所知,如果要在DoWork()
中访问,我可以在UI控件上使用Invoke()
方法。但是,这是否意味着以下每个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...");
}
您应该将worker代码与GUI代码分开。你在那个数据网格上没有DataSource
吗?基本上,您应该首先从网格(选定的行)中获取所需的数据,并将它们从GUI代码(按钮单击或其他)传递给后台worker。然后,您可以通过BackgroundWorker.ReportProgress
(它在GUI线程上执行进度事件)报告工作进度。
如果您不能将GUI代码与工作代码完全解耦,那么您将不得不使用Invoke
来完成其余部分。但是,从您的代码中不清楚为什么需要这样做,ReportProgress
应该足够了。
你的标题问题的一般答案是:是的。
如果一个后台工作线程需要访问一个UI项目,它只能通过Invoking
的一个方法来实现。
将数据从BW放入DGV的问题最好通过以解耦的方式访问其数据源来解决。我刚刚做了一个小测试,看看我的建议是否从原来的帖子工作,它看起来很好。
因此,与位图一样,您可以创建两个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
事件中被重新分配;我这样做是正确的,同时使display panel1:
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 insert,我不知道它是如何关联的。这个答案只是关于异步地从某处获取数据并将它们发送到UI。
从DGV获取数据到数据库并不是在BW线程中发生的事情,至少不是在传入更改时被触发的那个。如果您使用DGV作为输入,并发性将是一个问题!!如果你想预定的座位在你按回车键的时候已经有人了,那就够糟糕的了。但这是无法阻止的。但是,您需要确保输入不会被传入的更改所清除。噢,要是有信号就好了。
并发是棘手的!
由于控件运行在UI线程上,而不是在后台工作线程上,因此需要调用它们的所有函数