使用Task工厂传递方法参数(组合框文本)

时间:2014-09-11 09:10:37

标签: c# winforms task

所以我在这里有点困惑。我的代码如下所示:

task1 = Task.Factory.StartNew(() => dataTabel_b1 = ConverteerRoutines.GetDataTableVanDatabase(Properties.Settings.Default.connectionString_b1, cbSqlTabel_b1.Text));

第二个参数(cbSqlTabel_b1)是我GUI上的组合框。当我运行此代码时,我得到一个invalidoperationexcepction。我将收到此消息:不允许通过进程的不同线程进入:它从与创建该元素的线程不同的线程授予对控件cbSqlTabel_b1的访问权。我会这样做,它运作得很好:

String tableName = cbSqlTabel_b1.Text;
task1 = Task.Factory.StartNew(() => dataTabel_b1 = ConverteerRoutines.GetDataTableVanDatabase(Properties.Settings.Default.connectionString_b1, tableName));

所以我用文本框尝试了同样的事情。这看起来如下:

task1 = Task.Factory.StartNew(() => dataTabel_b1 = ConverteerRoutines.GetDataTableVanDatabase(Properties.Settings.Default.connectionString_b1, textBox1.Text));

这也很好。有人知道为什么我不能用comboBox做同样的事情吗?提前谢谢!

2 个答案:

答案 0 :(得分:1)

文本框的Text属性实现自动编组到UI线程以访问文本框。组合框的实现不会为您做到这一点,因此您需要确保自己从UI线程访问它,而不是依赖Text属性来从非UI线程工作。这样做的最佳方法是在开始新任务之前从UI中提取值。

答案 1 :(得分:-1)

访问UI元素时,您需要使用Control.Invoke,而您不在UI线程上。由于您要创建的任务是在单独的线程上运行,因此您需要使用Control.Invoke

在你的第二个例子中:

String tableName = cbSqlTabel_b1.Text;
task1 = Task.Factory.StartNew(() => dataTabel_b1 = ConverteerRoutines.GetDataTableVanDatabase(Properties.Settings.Default.connectionString_b1, tableName));

如果它在UI线程上运行(例如,它是从UI事件触发的),它将起作用,因为您正在从UI线程中点击cbSqlTabel_b1

我不确定为什么你的第三个示例文本框正在运行,也许有比我更多经验的人可以回答这个问题。

正如您在上述评论中发现和Servy一样,您可以通过在创建任务之前仍然在UI线程中获取值来修复它(这更有效,因为下面的内容意味着您正在跳跃一直都是线程。)

无论如何,为了让它在您的任务中运行,我建议您在UI类/表单中创建一个单独的方法来获取控件的文本,这将让您忘记尝试执行{{1}再次访问某些内容。

模式如下:

ControlInvoke

private string GetCbSqlTabel_B1Text() { if(this.InvokeRequired) { return (string)this.Invoke(new Func<string>(() => GetCbSqlTabel_B1Text())); } else { return cbSqlTabel_B1.Text; } } 执行它所说的内容,它会告诉您是否需要使用InvokeRequired。当从某个随机线程调用此方法时,它将从UI线程中调用自身,此时它知道它可以直接访问您需要的控件。

一旦你获得了这种方法,你只需要改变你的任务就可以使用它了:

Control.Invoke

我知道这种模式的唯一缺点是你可能最终需要创建很多这些方法,但它们的工作效果非常好。他们也很乐意将事物分配给控件,例如:

Task.Factory.StartNew(() =>
        {
          ConverteerRoutines.GetDataTableVanDatabase(Properties.Settings.Default.connectionString_b1, GetCbSqlTabel_B1Text()));
        });

这次我们不需要返回任何内容,因此我们只使用private void SetCbSqlTabel_B1Text(string text) { if(this.InvokeRequired) { this.Invoke(new Action(() => SetCbSqlTabel_B1Text(text))); } else { cbSqlTabel_B1.Text = text; } } 代替Action