这是否意味着必须在每个UI控件上调用Invoke()方法? [英] Does it mean that Invoke() method has to be called on each UI control?

查看:252
本文介绍了这是否意味着必须在每个UI控件上调用Invoke()方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是对数据库表进行Insert的方法.我在BackGroundWorker线程的DoWork()中调用此方法.它显然引发了跨线程操作无效..."错误.据我了解,如果要在DoWork()中访问控件,可以在UI控件上使用Invoke()方法.但是这是否意味着必须调用以下每个UI控件?有没有更好的方法来实现这一目标?

Following is a method that does Insert into the database table. And I am calling this method within DoWork() of BackGroundWorker thread. It obviously throws me the "cross thread operation not valid..." error. As I understand, I could use Invoke() method on UI controls if those to be accessed within DoWork(). BUT does it mean each of the following UI controls should have to be invoked? Is there a better way to achieve this?

    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...");
    }

推荐答案

您的标题问题的一般答案是:是.

The general answer to your title question is: Yes.

如果后台工作线程需要访问UI项,则只能通过Invoking一种方法来访问它.

If a background worker thread needs to access a UI item it can only do so by Invoking a method of it.

通过BW将数据从BW放入DGV的问题最好通过解耦方式访问其DataSource来解决.我只是做了一点测试,看看我从原始帖子中获得的建议是否可行,并且看起来还不错.

The problem of putting data into a DGV from a BW is best addressed by accessing its DataSource in a de-coupled way. I just did a little test to see if my suggestion from the original post works and it looks fine.

因此,就像使用位图一样,您可以创建两个DataSources作为属性;一个由后台工作人员填补,另一个用作DGV的Datasource.

So, like with the Bitmap, you can create two DataSources as Properties; one to fill from the background worker and one to use as the DGV's Datasource.

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

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

In the BW thread DoWork() you fill the theSeats_buffer list by calling an appropriate function void or bool getSeatData() and when you are done with the workload you pass the data into the theSeats_DS, maybe like this:

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

同样,此操作必须是线程安全的,可能是通过锁定接收列表theSeats_DS.

Again, this operation must be made thread-safe, probably by locking the receiving list theSeats_DS.

由于已重新创建DataSource,因此应在bw_RunWorkerCompleted事件中将其重新分配;我在使显示面板无效的同时做到了这一点:

since the DataSource has been re-created it should be re-assigned in the bw_RunWorkerCompleted event; I did it right along with invalidating the 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; }
}

关于数据库插入,我不知道它的关系.这个答案只是关于异步地从某处获取数据并将其发送到UI.

With regard to the DB Inserts, I don't know how it relates. This answer is only about getting data from somewhere asynchrously and sending them to the UI.

但是,从DGV到数据库中的数据并不是在BW线程中发生的,至少不是在传入更改时触发的数据.如果您使用DGV进行输入,那么并发将成为问题!!如果您尝试保留的座位实际上在您按Enter时就已经占据了,那已经很糟糕了.但这是无法避免的.但是,您需要确保输入不会因传入的更改而消失..OTOH,信号会很好..

Getting data from the DGV into the database is not something that would happen in the BW thread though, at least not the one that gets triggered upon incoming changes. If you use the DGV for input, concurrency will be an issue!! It is bad enough, if the seat one tries to reserve is actually taken by the time you press enter. But that can't be prevented. However, you need to ensure that the input isn't wiped out by incoming changes.. OTOH, a signal would be nice..

并发性很棘手!

这篇关于这是否意味着必须在每个UI控件上调用Invoke()方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆