访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。” 此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。注意 可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。 */using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; namespace SetTextSafe { public partial class Form1 : Form { public Form1() { InitializeComponent(); } // This delegate enables asynchronous calls for setting // the text property on a TextBox control. delegate void SetTextCallback(string text); // This thread is used to demonstrate both thread-safe and // unsafe ways to call a Windows Forms control. private Thread SetTextThread = null; // This event handler creates a thread that calls a // Windows Forms control in a thread-safe way. private void setTextSafeBtn_Click( object sender, EventArgs e) { this.SetTextThread = new Thread(new ThreadStart(this.ThreadProcSafe)); this.SetTextThread.Start(); } // This method is executed on the worker thread and makes // a thread-safe call on the TextBox control. private void ThreadProcSafe() { this.SetText("This text was set safely."); } // This method demonstrates a pattern for making thread-safe // calls on a Windows Forms control. // // If the calling thread is different from the thread that // created the TextBox control, this method creates a // SetTextCallback and calls itself asynchronously using the // Invoke method. // // If the calling thread is the same as the thread that created // the TextBox control, the Text property is set directly. private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. if (this.textBox1.InvokeRequired) { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } } } } 你会遇到这个问题的,我的体会就是windows通过自己的消息机制来通知委托代理者安全的来做你想做的事
/*这个程序例子是为了以安全的方式调用textbox,设置它的text属性
MSDN:对 Windows 窗体控件进行线程安全调用
访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。” 此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。注意
可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。
*/using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace SetTextSafe
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
} // This delegate enables asynchronous calls for setting
// the text property on a TextBox control.
delegate void SetTextCallback(string text); // This thread is used to demonstrate both thread-safe and
// unsafe ways to call a Windows Forms control.
private Thread SetTextThread = null; // This event handler creates a thread that calls a
// Windows Forms control in a thread-safe way.
private void setTextSafeBtn_Click(
object sender,
EventArgs e)
{
this.SetTextThread =
new Thread(new ThreadStart(this.ThreadProcSafe)); this.SetTextThread.Start();
} // This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{
this.SetText("This text was set safely.");
} // This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control.
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly. private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
}
}
你会遇到这个问题的,我的体会就是windows通过自己的消息机制来通知委托代理者安全的来做你想做的事
楼主也可以从这个角度思考
委托的签名(返回值,参数类型)确定了特定的方法
凡是符合这个委托签名的方法都可以把这个委托作为参数类型从而当作另外一个方法的参数
例如:……
delegate void dele(string s);///定义委托void method(string ss)///符合委托签名的方法
{
///
}dele d=new dele(method);///实例化委托void method2(dele d)///"dele"类型的参数
{
d("参数");///相当于调用method("参数");
}
个人认为这样有助于理解
讲到委托,可能一些人对委托的概念还有些模糊。那么委托到底是什么呢?熟悉C或C++的朋友对函数指针应该相当清楚。C#里的委托与之就有同等功效,不过委托是面象对象的,类型安全的。简单的说,委托就是对方法的引用的一种手断。那么什么时候使用委托呢?有些情况下,我们需要在一个类中使用另一个类的方法。很多人能够想到的是,我在这个类中创建另一个类的一个实例,不就可以顺理成章的调用它的方法了么?事实上确实也是这样,但是。并非所有情况都可以这样做!!
举个例子:我有一个主窗体(MainForm),由它创建并打开一个子窗体(SubForm)。我希望在子窗体上做某些操作的时候主窗体(MainForm)上的某些元素会发生变化。而这些变代并不要在等到关闭子窗体之后。比如,我希望在子窗体上双击某条数据,而该条数据就会自动被填写到主窗体的特定位置。想想这时候上叙的那种方法还会行得通吗?试都不会试,肯定是不行的。这时个委托就是最好的先择。可以在子窗体上定义一个委托,来指向主窗体上的一个方法。并在子窗体上双击了某条数据的时候执行这个委托。
定义委托:以delegate关键字来定义委托,委托的签名跟方法的签名一样,有返回类型、委托名,参数。委托的实现在System.Delegate类中,所以。当定义了一个委托时,它自动从System.Delegate继承。也就是说:委托是只需要你定义就可以使用,而并不需要你去关心它的实现。下为委托定义示例:
private delegate double MethodList(int _num1, int _num2);
public delegate void MethodNull();
从上面可以看出来,委托的签名与方法的签名一样,以什么样的签名形式定义委托,决定委托能接受哪种定义形式的方法。比如:上例中第一个委托能接受返回double类型,并且有两个int参数的方法。而第二个委托则接受不返回任何值,也没有任何参数的方法下面就以前面举的那个例子的需求来说演示一下委托的使用,要说明的是,我这里只演示关键的委托实现细节部分。并不将所有代码写出[我也没这样的精力(^_^)]。首先,我们看看在子窗体中的部份:
///比如我们定义CallMainFormMethod委托来执行主窗体上的方法
public delegate void CallMainFormMethod(string _RsCode);
public class SubForm : Form
{
public CallMainFormMethod ExeManFormMethod; //创建一个委托以执行方法
public SubForm()
{
InitializeComponent();
} ////这里是在dataGridView1上选中某行数据,并双击的时候执行委托。
private void dataGridView1_DoubleClick(object sender, EventArgs e)
{
if(this.dataGridView1.SelectedRows.Count > 0)
{
if(this.ExeManFormMethod != null) //这个主要防止在没有给委托传递方法时出错
this.ExeManFormMethod(this.dataGridView1.SelectedRows[0].Cells["DataCode"].Value.ToString());
}
}
}好现在来看看在主窗体MainForm中创建并打开子窗体(SubForm)时的代码:
public class MainForm : Form
{
private SubForm __SubForm; public MainForm()
{
InitializeComponent();
} private void AddData(string _dataCode)
{
////此方法执行将数据录入到主窗体的特定位置
} ////创建并打开子窗体的事件
private void mentItem1_Click(object sender, EventArgs e)
{
if(this.__SubForm != null && !this.__SubForm.IsDisposed)
this.__SubForm.Dispose();
this.__SubForm = new SubForm();
this.__SubForm. ExeManFormMethod = new CallMainFormMethod(this.AddData);
this.__SubForm.Show();
}
}
好了,实现前面所说例子的功能的原理大概就是这样。希望各位不要偷懒,详细理解这个演示所彩用的实现原理。然后自己写代码来实践一番。当然,委托的运用是很广泛的。我这里只是举了个及典型的例子,希望能给对委托概念尚不是很清淅的朋友一点线索。更多的运用还得各位自己在日后开发中跟据自己的理解自行变通。多播委托:
也可以叫它多路委托,委托是一个可以接受与其签名形式相符的方法引用的列表。我们可以用一个委托指向多个方法,只要这些方法都位于同一个应用程序域,就不需要管它来自何处。多播委托是一次性执行多个方法非常有效并且简洁的手断。比如:我做在一个窗体上作操作,所希这个操作能响影已有的几个窗体,下在说明如何给委托添加多个方法和移除委托中已有的方法:
以前面代码为基:////向委托添加多个方法
this.__SubForm. ExeManFormMethod += new CallMainFormMethod(this.AddData);
this.__SubForm. ExeManFormMethod += new CallMainFormMethod(this.InsertData);////向委托中移除方法
this.__SubForm. ExeManFormMethod -= new CallMainFormMethod(this.AddData);
this.__SubForm. ExeManFormMethod -= new CallMainFormMethod(this.InsertData);
注
1.委托的定义和方法的定义类似,只是在前面加了一个delegate,但委托不是方法,它是一种类型。是一种特殊的类型,看成是一种新的对象类型比较好理解。用于对与该委托有相同签名的方法调用。
2.委托相当于C++中的函数指针,但它是类型安全的。
3.委托是从System.Delegate派生,但不能象定义常规类型一样直接从System.Delegate派生,对委托的声明只能通过上面的声明格式进行定义。关键字delegate通知编译器这是一个委托类型,从而在编译的时候对该类进行封装,对这一过程C#定义了专门的语法来处理这一过程。
4.不能从一个委托类型进行派生,因为它也是默认sealed的
5.委托即可以对静态方法进行调用也可以对实例方法进行调用。
6.每个委托类型包含一个自己的调用列表,当组合一个委托或从一个委托中删除一个委托时都将产生个新的调用列表。
7.两个不同类型的委托即使它们有相同的签名和返回值,但还是两个不同类型的委托。但其实在使用中可看作是相同的。
委托的比较C#中对委托定义了两个操作符 == 和 !=
在以下情况下两个委托是相等的:
1.当两个委托都同时为null的时候
2.当两个委托都不为null时,下列情况下是相等的。
a.当两个委托的各自的调用列表只含有一个入口点的时候
在下列情况下是相等的
(1) 调用同一对象的同一静态方法
(2) 调用同一对象的同一实例方法
b.当两个委托具有多个入口点时
在下列情况下是相等的
(1)只有当它们调用列表中的调用的方法按顺序都一一对应相同的对象及对象的同一方法的时候如上所述的两个不同类型的委托但是它们具有相同的签名和返回值时,只要满足上述条件的,即使它们类型不同,但比较的结果也是相同的。委托的异常处理当调用该委托的方法中发生了异常时,首先在调用该委托的方法中搜寻catch语句块。如果没有,则去该委托调用的方法中去寻找有没有catch语句块,这和调用方法发生异常的处理是一样的。当调用一个为null的委托即委托中列表中不存在调用方法时,将发生NullRefrenceException委托的注意点:
当一个委托有多个入口点的时候,调用委托将依该委托的调用列表中的方法的顺序依次调用.这些方法共享一个参数集合,所以当委托有返回值的时候调用完这个委托后的返回值是最后一个方法的返回值或是有out参数.如果该委托的参数为ref(引用类型),那么在招待第一个方法的时候如果对这个参数的值有所改变,那么这个改变将会影响到后面的方法调用.委托的一个例子using System;
using System.Collections.Generic;
using System.Text;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// 创建一个委托实例,封装C类的静态方法M1
MyDelegate d1 = new MyDelegate(C.M1);
d1("D1"); // M1 // 创建一个委托实例,封装C类的静态方法M2
MyDelegate d2 = new MyDelegate(C.M2);
d2("D2"); // M2 // 创建一个委托实例,封装C类的实例方法M3
MyDelegate d3 = new MyDelegate(new C().M3);
d3("D3"); // M3 // 从一个委托d3创建一个委托实例
MyDelegate d4 = new MyDelegate(d3);
d4("D4"); // M3 // 组合两个委托
MyDelegate d5 = d1 + d2;
d5 += d3;
d5("D5"); // M1,M2,M3 // 从组合委托中删除d3
MyDelegate d6 = d5 - d3;
d6("D6"); // M1,M2
d6 -= d3; // 虽然d6调用列表中已经没有d3了,但这样只是不可能的移除没有错误发生
d6("D6"); // M1,M2
d6 -= d6;
//d6("D6"); 此时d6的调用列表为空,d6为null,所以引发System.NullReferenceException MyDelegate d7 = new MyDelegate(C1.P1);
d7("D7"); // C1.P1 MyDelegate d8 = new MyDelegate(new C2().P1);
d8("D8"); // C2.P1 }
} // 声明一个委托MyDelegate
public delegate void MyDelegate(string str); public class C
{
public static void M1(string str)
{
Console.WriteLine("From:C.M1: ", str);
} public static void M2(string str)
{
Console.WriteLine("From:C.M2: ", str);
} public void M3(string str)
{
Console.WriteLine("From:C.M3: ", str);
}
} public class C1
{
public static void P1(string str)
{
Console.WriteLine("From:C1.P1: ", str);
}
} public class C2
{
public void P1(string str)
{
Console.WriteLine("From:C2.P1: ", str);
}
}
}事件委托事件概述事件就是当对象或类状态发生改变时,对象或类发出的信息或通知。发出信息的对象或类称为"事件源",对事件进行处理的方法称为"接收者",通常事件源在发出状态改变信息时,它并不知道由哪个事件接收者来处理.这就需要一种管理机制来协调事件源和接收者,C++中通过函数指针来完成的.在C#中事件使用委托来为触发时将调用的方法提供类型安全的封装
事件的声明1.声明一个委托
public delegate void EventHandler(object sender, System.EventArgs e);2.声明一个事件
public event EventHandler Changed;3.引发一个事件
public OnChanged(EnventArgs e)
{
if ( Changed != null)
{
Changed(this,e);
}
}4.定义事件处理程序
public MyText_OnChanged(Object sender,EventArgs e)
5.订阅事件(将事件处理程序添加到事件的调用列表中)myText.Changed += EventHandler(MyText_OnChanged);下面的一个小例子说明了怎样定义一个完整的事件机制:using System;
using System.Collections.Generic;
using System.Text;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyText myText = new MyText(); // 将事件处理程序添加到事件的调用列表中(即事件布线)
myText.Changed += new MyText.ChangedEventHandler(myText_Changed);
string str = "";
while (str != "quit")
{
Console.WriteLine("please enter a string:");
str = Console.ReadLine();
myText.Text = str;
}
} // 对Change事件处理的程序
private static void myText_Changed(object sender, EventArgs e)
{
Console.WriteLine("text has been changed :n" ,((MyText)sender).Text);
}
} public class MyText
{
private string _text = ""; // 定义事件的委托
public delegate void ChangedEventHandler(object sender, EventArgs e); // 定义一个事件
public event ChangedEventHandler Changed; // 用以触发Change事件
protected virtual void OnChanged(EventArgs e)
{
if (this.Changed != null)
this.Changed(this, e);
} // Text属性
public string Text
{
get { return this._text; }
set
{
this._text = value;
// 文本改变时触发Change事件
this.OnChanged(new EventArgs());
}
}
}
}
http://topic.csdn.net/u/20080626/17/1742fccb-1513-4543-9f9f-045660d24666.html