在第一篇中的最后,我提到了,要在
public Form1()
{
InitializeComponent();
//Form.CheckForIllegalCrossThreadCalls = false;
}
里面加上Form.CheckForIllegalCrossThreadCalls = false;之后,才不会以报错,txtNum不是此线程建立的错误。其实我们可以试一下,就是你不用VS的调试打开,而是直接运行.exe文件。就不会报这个错了。
Form.CheckForIllegalCrossThreadCalls什么意思呢。
MSDN上给出的解释是:如果捕获了对错误线程的调用,则为 true ;否则为 false 。
我们只所以要把它设置为false就是为了让他捕获这个错误,这样如果一旦发生了线程之间的错误使用,这个后果是很严重的。
如果你想了解一下,为什么会这样
http://www.cnblogs.com/yuyijq/archive/2010/01/11/1643802.html 余昭辉的blog给出了很好的解释。
在这里我不在多说了。
我来说一下,如果我们不在构造函数中加这句,那怎么解决这个错误呢。
还记得我说过的吗。基本上每个方法都会有一个异步的方法。就是前面加一个begin。在每个控件的属性方法中也会有一个这种方法,叫什么呢
还记得昨天的程序,委托中是不是有一个BeginInvoke吗。基本上每个控件都会有一个这个方法。我们就用它来完成。
还记得第一篇里的那个被委拖调用的方法。我们来改一下它
在使用改变它之前,我们还要定义一个委托和一个方法。为什么呢。
通过线程调用控件的方法。例如,您可能要在窗体上调用一个禁用按钮或更新显示的方法来响应某个线程执行的操作。.NET Framework 提供从任何线程都可安全调用的方法,以调用与其他线程所拥有的控件进行交互的方法。Invoke 方法允许同步执行控件上的方法,而 BeginInvoke 方法则初始化异步执行。要使用这些方法,必须用与将调用的方法相同的签名声明委托。然后,您可以通过向要调用的方法提供适当的委托来调用窗体上任何控件的 Invoke 或 BeginInvoke 方法。
我想大家看了上面这段话,就都会明白为什么了吧。想要安全的从任何线程调用,就要用到beginInvoke了。
好,我们现在再定义一个委托和委托要调用的方法。
public delegate void txtGetNums();
这个就不多解释了,再写一个函数
public void txtNums()
{
this.txtNum.Text = (Convert.ToInt32(this.txtNum.Text.Trim()) + 1).ToString();
}
还记得我说的吗。你定义的函数必须与委托相对应。
注意:细心的朋友一定会看到,这里你没有加数啊。只是在加1,对。因为我没有传值。哈哈。。。。。。这里我打算留到下节再说。
好了,现在我们来修改一下昨天的那个委托调用的函数。
/// <summary>
/// 无返回值调用方法,用来计数,这里我用了一个Thread.sleep(500)这个大家都应该明白是什么意思,就是让它暂停半秒钟
/// 只有这样,我们才能看到它的变化。
/// </summary>
public void GetNum()
{
for (int i = 1; i <= 10; i++)
{
//this.txtNum.Text = (Convert.ToInt32(this.txtNum.Text.Trim()) + i).ToString();
this.txtNum.BeginInvoke(new txtGetNums(txtNums));
Thread.Sleep(500);
}
//this.txtInfo.Text = "结束计数";
this.txtInfo.BeginInvoke(new txtGetNums(endtxtNums));
}
看到了吧,昨天写的代码,我注掉了。换成了现在这样的
最后一行this.txtInfo.BeginInvoke(new txtGetNums(endtxtNums));里面的endtxtNums函数,朋友你自己试试写写。应该写成什么样呢。
大家现在再来试试。运行一下,看结果怎么样。