2007年12月4日 #
事件是类和对象向外界发出的消息,事件的执行是通过事件委托的方式,调用我们所准备好的处理方法,而是先消息的响应的。要响应某些事件并针对某些事件执行我们意定的方法,需要做到以下几步:
1、声明事件委托。
2、声明事件。
3、添加事件的触发方法。
4、添加事件的处理程序(响应事件的方法)。
5、将指定的事件处理程序邦定到要处理的事件上(订阅事件)。
6、用户信息操作,并触发事件(调用事件的触发方法)。
7、通过事件委托的回调,执行我们需要的事件处理程序。
下面我们举一个简单的自定义事件处理程序的例子(控制台程序)
namespace 事件
{
//发布事件的类
public class TestEventSource
{
//定义事件参数类
public class TestEventArgs : EventArgs
{
public readonly char KeyToRaiseEvent;
public TestEventArgs(char keyToRaiseEvent)
{
KeyToRaiseEvent = keyToRaiseEvent;
}
}
//定义delegate
public delegate void TestEventHandler(object sender, TestEventArgs e);
//用event 关键字声明事件对象
public event TestEventHandler TestEvent;
//事件触发方法
protected virtual void OnTestEvent(TestEventArgs e)
{
if (TestEvent != null)
TestEvent(this, e);
}
//引发事件
public void RaiseEvent(char keyToRaiseEvent)
{
TestEventArgs e = new TestEventArgs(keyToRaiseEvent);
OnTestEvent(e);
}
}
//监听事件的类
public class TestEventListener
{
//定义处理事件的方法,他与声明事件的delegate具有相同的参数和返回值类型
public void KeyPressed(object sender, TestEventSource.TestEventArgs e)
{
Console.WriteLine("发送者:{0},所按得健为:{1}", sender, e.KeyToRaiseEvent);
}
//订阅事件
public void Subscribe(TestEventSource evenSource)
{
evenSource.TestEvent += new TestEventSource.TestEventHandler(KeyPressed);
}
//取消订阅事件
public void UnSubscribe(TestEventSource evenSource)
{
evenSource.TestEvent -= new TestEventSource.TestEventHandler(KeyPressed);
}
}
//测试类
public class Test
{
public static void Main()
{
//创建事件源对象
TestEventSource es = new TestEventSource();
//创建监听对象
TestEventListener el = new TestEventListener();
//订阅事件
Console.WriteLine("订阅事件\n");
el.Subscribe(es);
//引发事件
Console.WriteLine("输入一个字符,再按enter键");
string s = Console.ReadLine();
es.RaiseEvent(s.ToCharArray()[0]);
//取消订阅事件
Console.WriteLine("\n取消订阅事件\n");
el.UnSubscribe(es);
//引发事件
Console.WriteLine("输入一个字符,再按enter健");
s = Console.ReadLine();
es.RaiseEvent(s.ToCharArray()[0]);
}
}
}
程序执行结
订阅事件
输入一个字符,再按enter键
aaaa
发送者:事件.TestEventSource,所按得健为:a
取消订阅事件
输入一个字符,再按enter健
TestEventSource类。他就相当于windows控件类一样,是事件的源,里面包含有事件的声明,以及存储调用参数的事件参数类,以及事件的触发方法。
TestEventListener类。他提供了事件处理程序,并实现了事件处理程序和事件对象的邦定,当然时间处理程序可以放在别处, 跟邦定程序(订阅事件)放在一起便于理解和调用
Test 类,实例化自定义事件的事件源对象,并调用 TestEventListener类中的Subscribe(es);方法进行事件对象和事件处理程序的邦定(订阅事件),调用 TestEventSource类中的RaiseEvent(char keyToRaiseEvent)引发对象,并有对象所指定的委托回调处理事件。完成整个自定义事件。
其中 RaiseEvent(char keyToRaiseEvent) 就相当于main()一样是自定义事件的执行入口, 从这个法开始---〉调用事件委托----〉查找订阅事件程序找到事件所封装的方法集----〉由委托回调事件处理程序并传递参数---〉执行事件处理程序。
委托(delegate)是一种引用类型,我们在处理他的时候要当作类来看待而不是方法,说白了委托就是对方法或者方法列表的引用,调用一个委托实例就好像是调用c++中的指针一样,他封装了对制定方法的引用,或者说委托起到的是桥梁的作用,实例后的委托对象会将给定的参数传递给他所回调的方法,并去执行方法。
看一个简单的例子:
//声明一个委托
delegate int myDelegateHandler(int a, int b);
public class A
{
//静态的处理方法
public static int M1(int a, int b)
{
int c = 0;
c = a + b;
return c;
}
}
//入口类
public class B
{
public static void Main()
{
//实例一个委托
myDelegateHandler mdh = new myDelegateHandler(A.M1);
//调用委托
int sum = mdh(2, 2);
Console.WriteLine(sum.ToString());
}
}
上面是一个非常简单的例子,具体的实现步骤:
1、 先声明个委托实例 ;
2、然后提供要处理的方法;
3、再实例化委托(把委托看作是类的话,实例化委托就不难理解了,其参数是要处理的方法,这里的方法 不用加括号,实例化的过程就是装载方法的过程,就好像需要参数的构造函数一样)实例化后的委托对象就好比是c++中的指针,它本身就是封装了方法的对象;
4、最后我们调用委托对象就好比是调用了被封装方法本身,调用时的参数也就传给了被封装的方法。
5、需要注意的是 所声明的委托无论是 参数个数,参数类型,返回值类型 都要和所要封装的方法保持一致,当调用委托实例对象时,所传入的参数也要保持一致 ,否则会出现错误。
委托链
我们知道委托是对方法的封装,而且委托可以封装很多方法形成委托链,其实委托就好像是一个容器,他封装了我们想要实现的若干方法,当调用委托对象(相当于c++中的指针)时,就会顺序的执行它所封装的所有的方法,如果有返回值的话,往往返回的是最后一个被执行的方法的返回值,委托链的形成可以用"+="或"-="对不同的委托实例进行二元操作。
委托链实例:
//定义一个委托
public delegate void PrintHandler(string message);
public class PrintProvider1
{
public void Print(string msg)
{
Console.WriteLine( msg + "1111111");
}
}
public class PrintProvider2
{
public void Print(string msg)
{
Console.WriteLine( msg + "2222222");
}
}
class Delegate1
{
public static void StaticPrint(string msg)
{
Console.WriteLine(msg + "3333333");
}
public static void Main()
{
string s = "委托链";
PrintProvider1 pp1 = new PrintProvider1();
PrintProvider2 pp2 = new PrintProvider2();
//创建委托实例
PrintHandler prn1 = new PrintHandler(pp1 .Print );
PrintHandler prn2 = new PrintHandler(pp2 .Print );
PrintHandler prn3 = new PrintHandler(StaticPrint );
Console.WriteLine("委托实例prn1的打印结果");
prn1(s );
Console.WriteLine("委托实例prn2的打印结果");
prn2(s);
Console.WriteLine("委托实例prn1+prn2的打印结果");
PrintHandler prn = prn1 + prn2;
prn(s);
Console.WriteLine("委托实例prn1+prn2+prn3的打印结果");
prn += prn3;
prn(s);
Console.WriteLine("委托实例prn1+prn3的打印结果");
prn -= prn2;
prn(s);
Console.WriteLine("委托实例prn3的打印结果");
prn -= prn1;
prn(s);
Console.WriteLine("试图调用null委托会引发异常");
try
{
prn -= prn3;
prn(s);
}
catch (NullReferenceException ex)
{
Console.WriteLine(ex .Message );
}
Console.WriteLine("试图从null中移出委托是无效操作");
try
{
prn -= prn3;
}
catch (NullReferenceException ex)
{
Console.WriteLine(ex .Message );
}
Console.Read();
}
}
执行结果
委托实例prn1的打印结果
委托链1111111
委托实例prn2的打印结果
委托链2222222
委托实例prn1+prn2的打印结果
委托链1111111
委托链2222222
委托实例prn1+prn2+prn3的打印结果
委托链1111111
委托链2222222
委托链3333333
委托实例prn1+prn3的打印结果
委托链1111111
委托链3333333
委托实例prn3的打印结果
委托链3333333
试图调用null委托会引发异常
未将对象引用设置到对象的实例。
试图从null中移出委托是无效操作
例如 dropdownlist1中用"111,222,333",如果复制dropdownlist1.text="444"dropdownlist1.selectedvalue="444"时就会出现前面的异常。
但是有时候这种异常没有出现,那是因为我们没有用到dropdownlist或radiobuttonlist控件的(IsPostBack)indexchange事件,当控件需要事件时上面的错误异常就会被捕获。这是因为SelectedValue 属性还可以用于选择列表控件中的某一项,方法是用该项的值设置此属性。如果列表控件中的任何项都不包含指定值,则会引发 System.ArgumentOutOfRangeException。
同样这样 if ( DropDownList1.Items.FindByValue("中国").Selected)也会出现异常。
我们可以用selectedindex来解决上面的问题:
例如:DropDownList1.SelectedIndex = DropDownList1.Items.IndexOf(DropDownList1.Items.FindByValue("中国"));
这种方法在动态的赋值时先要检索控件列表中是否有匹配的数值,如果数据匹配则dropdownlist.text的值被赋为:“中国”,
如果通过FindByValue没有找到指定项则为null,而Items.IndexOf(null)会返回-1,这样我们就可以根据判断自动指出默认index的值了
这样就避免了异常的发生。
无论在做web还是在写winform程序是老是在导出excel数据是遇到科学计数法问题,如果字符太长(如身份证号)在导出的excel 文件中就会出现长字符串的科学计数法表示,反复导数据是就会出现错误 。
我解决的办法是在到处是或者存储将要导出时,每条记录字符串形式处理
在asp.net 中 我一般都是将要导出的数据放到gridview网格里,首先对网格邦定数据时 字符串形式处理,然后再用普通的形式导出excel就把问题解决了。
我的代码非常简单:在邦定gridview控件时在rowdatabound事件中队数据格式化
protected void gError_RowDataBound(object sender, GridViewRowEventArgs e)
{
//1) 文本:vnd.ms-excel.numberformat:@
//2) 日期:vnd.ms-excel.numberformat:yyyy/mm/dd
//3) 数字:vnd.ms-excel.numberformat:#,##0.00
//4) 货币:vnd.ms-excel.numberformat:¥#,##0.00
//5) 百分比:vnd.ms-excel.numberformat: #0.00%
for (int i = 0; i < e.Row.Cells.Count; i++)
{
if (e.Row.RowType == DataControlRowType.DataRow)
e.Row.Cells[i ].Attributes.Add("style", "vnd.ms-excel.numberformat:@");
}
}
然后执行到处操作就不会出现问题了
protected void btnOut_Click(object sender, EventArgs e)
{
Response.Clear();
Response.Buffer = true;
Response.Charset = "GB2312";
Response.AppendHeader("Content-Disposition", "attachment;filename=FileName.xls");
Response.ContentEncoding = System.Text.Encoding.UTF7;
//设置输出文件类型为excel文件。
Response.ContentType = "application/ms-excel";
System.IO.StringWriter oStringWriter = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter oHtmlTextWriter = new System.Web.UI.HtmlTextWriter(oStringWriter);
this.gError.RenderControl(oHtmlTextWriter);
Response.Output.Write(oStringWriter.ToString());
Response.Flush();
Response.End();
}
public override void VerifyRenderingInServerForm(Control control)
{
//base.VerifyRenderingInServerForm(control);
}
在winform程序开发时,处理的办法就是在导出的过程中,开始试了 处理excel对象的格式 mysheet.Cells.NumberFormat = "#";
后来没有成功。最后还是用了逐条纪录进行字符格式转化的方法,即添加“ ' ”.
我写得代码主要部分如下
#region 执行数据导出
try
{
//到导出excel
Excel.ApplicationClass my = new Excel.ApplicationClass();
if (my == null)
{
MessageBox.Show("无法创建excel对象,可能您的系统没有安装excel");
return;
}
my.Visible = false;
Excel.Workbook mybook = (Excel.Workbook)my.Workbooks.Add(1);
((Excel.Worksheet)mybook.Worksheets[1]).Name = "sheet1";
Excel.Worksheet mysheet = (Excel.Worksheet)mybook.Worksheets[1];
// mysheet.Cells.NumberFormat = "#";
//导出列名
for (int j = 0; j < this.dgvShow.Columns.Count; j++)
{
if (this.dgvShow.Columns[j].Visible == true)
{
mysheet.Cells[1, j + 1] = "'" + Convert.ToString(this.dgvShow.Columns[j].HeaderText);//加"'"防止科 学计数法
}
}
//导出数据
for (int i = 0; i < this.dgvShow.Rows.Count; i++)
{
for (int j = 0; j < this.dgvShow.Columns.Count; j++)
{
mysheet.Cells[i + 2, j + 1] = "'" + Convert.ToString(this.dgvShow.Rows[i].Cells[j].Value);
}
}
if (savefilename != "")
{
try
{
//mybook.Save();
mybook.SaveCopyAs(savefilename);
MessageBox.Show("excel文件导出成功!");
}
catch (Exception ex)
{
MessageBox.Show("导出文件出现错误,文件可能正被打开!\n" + ex.Message);
}
}
GC.Collect();
}
catch (Exception ex)
{
MessageBox.Show("数据导出时出现错误,一下是详细错误信息:\n" + ex.Message);
return;
}
#endregion
每条记录都进行处理 ,如果数据量很多的话应该会影响到速度,一定还有很多更好的方法,一起学习提高。
无论在做web还是在写winform程序是老是在导出excel数据是遇到科学计数法问题,如果字符太长(如身份证号)在导出的excel 文件中就会出现长字符串的科学计数法表示,反复导数据是就会出现错误 。
我解决的办法是在到处是或者存储将要导出时,每条记录字符串形式处理
{
//1) 文本:vnd.ms-excel.numberformat:@
{
if (e.Row.RowType == DataControlRowType.DataRow)
e.Row.Cells[i ].Attributes.Add("style", "vnd.ms-excel.numberformat:@");
}
{
Response.Clear();
Response.Buffer = true;
Response.Charset = "GB2312";
Response.AppendHeader("Content-Disposition", "attachment;filename=FileName.xls");
Response.ContentEncoding = System.Text.Encoding.UTF7;
Response.ContentType = "application/ms-excel";
System.IO.StringWriter oStringWriter = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter oHtmlTextWriter = new System.Web.UI.HtmlTextWriter(oStringWriter);
this.gError.RenderControl(oHtmlTextWriter);
Response.Output.Write(oStringWriter.ToString());
Response.Flush();
Response.End();
{
//base.VerifyRenderingInServerForm(control);
}
try
{
//到导出excel
Excel.ApplicationClass my = new Excel.ApplicationClass();
if (my == null)
{
MessageBox.Show("无法创建excel对象,可能您的系统没有安装excel");
return;
}
my.Visible = false;
Excel.Workbook mybook = (Excel.Workbook)my.Workbooks.Add(1);
((Excel.Worksheet)mybook.Worksheets[1]).Name = "sheet1";
Excel.Worksheet mysheet = (Excel.Worksheet)mybook.Worksheets[1];
// mysheet.Cells.NumberFormat = "#";
//导出列名
for (int j = 0; j < this.dgvShow.Columns.Count; j++)
{
if (this.dgvShow.Columns[j].Visible == true)
{
mysheet.Cells[1, j + 1] = "'" + Convert.ToString(this.dgvShow.Columns[j].HeaderText);//加"'"防止科 学计数法
}
}
//导出数据
for (int i = 0; i < this.dgvShow.Rows.Count; i++)
{
for (int j = 0; j < this.dgvShow.Columns.Count; j++)
{
mysheet.Cells[i + 2, j + 1] = "'" + Convert.ToString(this.dgvShow.Rows[i].Cells[j].Value);
}
}
{
try
{
//mybook.Save();
mybook.SaveCopyAs(savefilename);
MessageBox.Show("excel文件导出成功!");
}
catch (Exception ex)
{
MessageBox.Show("导出文件出现错误,文件可能正被打开!\n" + ex.Message);
}
}
GC.Collect();
}
catch (Exception ex)
{
MessageBox.Show("数据导出时出现错误,一下是详细错误信息:\n" + ex.Message);
return;
}
#endregion

