在什么条件下需要调用方法?

时间:2014-12-17 22:02:19

标签: c# winforms invoke

我认为我明白为了让一个单独的线程在winforms应用程序中对GUI进行更改,需要调用该方法。但是,我已经编写了一种异步填充组合框的方法,它向我展示了故事的更多内容。

以下是相关代码,省略了公司信息:

private List<string> ids = new List<string>();
private BindingSource bindingSource = new BindingSource();
//...
cboIds.DataSource = bindingSource;

private void GetAvailableIds()
{
    Task idTask = new Task(
        () =>
        {
            bindingSource.Add("Searching...");    //This always updates the UI 
                                                  //without invoking

            if (cboIds.InvokeRequired)    
            {
                Invoke((MethodInvoker)delegate
                {                                //This sometimes updates the UI without 
                    cboIds.Enabled = false;      //invoking, but sometimes fails, so I                                                     
                });                              //added the check
            }
            else
                cboIds.Enabled = false;

            List<string> temp = GetUpcomingIds();

            Invoke((MethodInvoker)delegate
            {
                cboIds.Enabled = true;
                bindingSource.Clear();
                foreach (string str in temp)
                     bindingSource.Add(str);  //This never works without invoking.  
            });                               //Why, if the same operation above 
        });                                   //always works without invoking?
    idTask.Start();
}

为什么BindingSource的初始添加不需要调用,将combobox.enabled设置为false有时需要调用,并且总是需要调用BindingSource的最终添加?如果它们都在同一个线程上,那么它们的行为是否应该相同?我错误地假设他们都在同一个线程上吗?

1 个答案:

答案 0 :(得分:0)

当需要Invoke()时,或者需要异步等效BeginInvoke()时:当您想要与线程中的任何Control类对象进行交互时,需要使用它除了拥有该对象的线程。 Control个对象由创建它们的线程拥有(此所有权归因于托管Control对象所代表的本机数据结构的底层线程关联)。

如果与提供的代码示例不同,您确实将BindingSource对象附加到某个控件,那么在执行修改绑定数据的该对象的任何成员时,您需要使用Invoke(),如果您在一个线程中执行该成员,而该线程不是拥有BindingSource绑定到的控件的线程。这是因为BindingSource对象将引发控件处理的事件;该交互必须在拥有该控件的线程中发生,并且您实现该操作的唯一方法是使用BindingSourceInvoke()的更新执行移动到拥有线程。方法

如果BindingSource对象开始没有附加到控件上,然后再附加,则对于对象的任何修改都不需要Invoke(),但不能在时间点之后它附在一个控件上。如果您的代码示例未完成,并且您实际上已将您创建的bindingSource实例附加到控件,但在第一次调用Add()方法后,这可以解释为什么您稍后需要Invoke(),但不能初次调用Add()

现在,考虑到所有这些......

请注意,对于DataSource绑定,如果您尝试直接更新控件,则不会像修改InvalidOperationException一样BindingSource。相反,绑定会延迟控件的更新,直到它能够执行,即在拥有控件的线程上更新BindingSource

这意味着在您的代码示例中,当您在调用第一个Add()方法时,控件立即更新时,您没有注意到这一点,因为您确实回到了在此之后立即拥有线程,此时可以更新控件。即从技术上讲,你需要使用Invoke()进行第一次更新,但这一切都发生得如此之快,以至于你没有注意到“正确的方式”和“正确的方式”之间的区别“错误的方式“。