C#中Invoke的用法

在用.NET Framework框架的WinForm构建GUI程序界面时,若是要在控件的事件响应函数中改变控件的状态,例如:某个按钮上的文本原先叫“打开”,单击以后按钮上的文本显示“关闭”,初学者每每会想固然地这么写:框架

void ButtonOnClick(object sender,EventArgs e)函数

{ui

    button.Text="关闭";this

}线程

这样的写法运行程序以后,可能会触发异常,异常信息大体是“不能从不是建立该控件的线程调用它”。注意这里是“可能”,并不必定会触发该种异常。形成这种异常的缘由在于,控件是在主线程中建立的(好比this.Controls.Add(...);),进入控件的事件响应函数时,是在控件所在的线程,并非主线程。在控件的事件响应函数中改变控件的状态,可能与主线程发生线程冲突。若是主线程正在重绘控件外观,此时在别的线程改变控件外观,就会形成画面混乱。不过这样的状况并不总会发生,若是主线程此时在重绘别的控件,就可能逃过一劫,这样的写法能够正常经过,没有触发异常。orm

正确的写法是在控件响应函数中调用控件的Invoke方法(其实若是你们之前用过C++ Builder的话,也会找到相似Invoke那样的激活到主线程的函数)。Invoke方法会顺着控件树向上搜索,直到找到建立控件的那个线程(一般是主线程),而后进入那个线程改变控件的外观,确保不发生线程冲突。正确写法的示例以下:事件

void ButtonOnClick(object sender,EventArgs e)开发

{io

    button.Invoke(new EventHandler(delegateobject

    {

        button.Text="关闭";

    }));

}

Invoke方法须要建立一个委托。你能够事先写好函数和与之对应的委托。不过,若想直观地在Invoke方法调用的时候就看到具体的函数,而不是到别处搜寻的话,上面的示例代码是不错的选择。

这样的写法有一个烦人的地方:对不一样的控件写法不一样。对于TextBox,要TextBoxObject.Invoke,对于Label,又要LabelObject.Invoke。有没有统一一点的写法呢?

主窗口类自己也有Invoke方法。若是你不想对不一样的控件写法不同,能够所有用this.Invoke:

void ButtonOnClick(object sender,EventArgs e)

{

    this.Invoke(new EventHandler(delegate

    {

        button.Text="关闭";

    }));

}

在C# 3.0及之后的版本中有了Lamda表达式,像上面这种匿名委托有了更简洁的写法。.NET Framework 3.5及之后版本更能用Action封装方法。例如如下写法能够看上去很是简洁:

void ButtonOnClick(object sender,EventArgs e)

{

    this.Invoke(new Action(()=>

    {

        button.Text="关闭";

    }));

}

以上写法每每充斥着WinForm构建的程序。

在微软新一代的界面开发技术WPF中,因为界面呈现和业务逻辑原生态地分开在两个线程中,因此控件的事件响应函数就没必要Invoke了。可是,若是手动开辟一个新线程,那么在这个新线程中改变控件的外观,则仍是要Invoke的。

相关文章
相关标签/搜索