c#事件到底怎么写,谁能给举个最简单的例子,包括定义事件,订阅事件,触发事件的完整流程
using System;namespace EventExample{ public delegate void MyEventHandler(Object sender, EventArgs e); public class EventExample { public event MyEventHandler FindSeven; private Random _NumberCreator = new Random(); public void MessageLoop() { while(true) { if(_NumberCreator.Next(8) == 7) { if(FindSeven != null) { EventArgs e = new EventArgs(); FindSeven(this, e); } break; } } } } class Program { static void Main(string[] args) { EventExample myEventControl = new EventExample(); myEventControl.FindSeven += new MyEventHandler(MyFindSeven); myEventControl.MessageLoop(); } public static void MyFindSeven(Object sender, EventArgs e) { Console.WriteLine("Find Seven"); } }}上面是一段简单的自定义事件处理代码首先解释一下委托。委托相当于c语言中的函数指针,用来引用参数相同但处理不同的函数,可以看作是 签名相同的函数 的共同别名。在事件处理中,它用来回调自定义的事件处理函数。上面的MyEventHandler委托可以用来代替所有参数为:第一个是Object类型、第二个为EventArgs类型,返回值为void的函数,例如Program类中的MyFindSeven函数。委托是一种类型结构,与类、结构体、枚举一样,因此它可以直接在命名空间中声明,不必放在某个类中。delegate 关键字相当于class关键字,MyEventHandler相当于EventExample。实例化委托时,可以以函数作为参数。例如MyEventHandler handler = new MyEventHandler(MyFindSeven)实例化了MyEventHandler委托。此后handler就可以代替MyFindSeven函数。调用函数时,handler(this,e)与MyFindSeven(this,e)等同。事件的设计主要包括:定义事件、编写事件处理代码、绑定处理函数等步骤。定义事件时,事件必须以成员的身份出现。定义事件使用event关键字,后面跟处理事件的委托类型(MyEventHandler),使用委托是语法规定,可以查看C#语法规范Ecma334中关于事件声明的描述。为了使事件的触发处理尽量简单,我将事件的触发和接收处理都放在了EventExample类中。类成员FindSeven表明EventExample类可以接收事件,成员函数MessageLoop用于触发FindSeven事件,当随机数为7时,触发事件。判断FindSeven是否为空是为了检查事件是否已经绑定自定义处理代码。主函数中,首先实例化事件引发和接收事件的类EventExample,接着为FindSeven绑定处理函数,最后调用MessageLoop触发事件。在事件中使用委托的好处在于,当事件触发时,底层代码可以直接调用委托,不用关心事件处理的自定义函数。EventExample相当于button一类的控件,事件触发时,.NET底层代码直接调用Click一类的委托,只要编程人员通过…Click += new EventHandler(事件处理函数)这种方式将自定义处理函数绑定到事件上,就可以保证编写的代码会参与到事件的处理中。关于C#中事件的详细描述,可以查看《C#入门经典》、《C#高级编程》、《CLR via C#》这些书籍。
如何使用JS触发DIV的onclick事件
自动触发点击事件,其实就让点击实现自己执行。
下面是简单的代码实现:
HTML 代码:
1
JS代码:
1
2
3
4
5
var oDiv = document.getElementById('div'); //获取元素div
oDiv.onclick = function(){ //给元素增加点击事件
alert(1);
};
oDiv.click(); //执行点击事件,这样就模拟出了自动执行点击事件。
MFC中如何延时 怎么写? 代码结构如下
要不影响其他代码的运行,就是说在延时的时候也要能够处理窗体消息。
否则程序就会没有响应。
如果你一定要按照这个代码结构,我提供一个思路:
1、使用内核可等待的计时器对象来实现延时。
CreateWaitableTimer创建
SetWaitableTimer设定延时时间
2、等待计时器延时不可使用WaitForSingleObject,使用MsgWaitForMultipleObjects等待,类型用QS_ALLEVENTS,一定要检查等待的返回值
3、对于返回值的处理,如果是WAIT_OBJECT_0,那么说明延时时间到了,继续运行后面的语句
如果返回WAIT_OBJECT_0 + 1,则说明有消息来了,不断调用PeekMessage,参数PM_NOREMOVE,如果返回非0,则调用AfxGetThread()->PumpMessage(),然后检查这个的返回值,返回0则调用AfxPostQuitMessage并结束循环退出函数,如果返回非0则继续下一轮调用PeekMessage
4、完成以后可以考虑关闭可等待的计时器内核对象
简单说就是,把原来只调用一个Sleep改为用一个定时器设置一个目标时间,然后等那个目标时间到来。在等的过程中同时还检查有没有消息来,有的话处理消息,这样窗口上的按钮啊啥的也都还能点(当然你别再点会有类似处理过程的按钮……),也不会提示没有响应,窗口要拖动也还能拖动。方法比较麻烦,但是确确实实不需要创建新线程,完全依靠内核的线程调度机制。
如果不一定要用这样的代码结构,那么把这些语句放入一个新的线程里面运行就好了。
我的textarea点击一下,写字的光标就在中间部位,怎么把光标移到前面去?
Javascript设置和获取Textarea的光标位置的方法,可定位光标到某个位置:效果如图:源码: JS设置及获取Textarea的光标位置 var isIE = !(!document.all); function posCursor(){ var start=0,end=0; var oTextarea = document.getElementById("textarea"); if(isIE){ //selection 当前激活选中区,即高亮文本块,和/或文当中用户可执行某些操作的其它元素。 //createRange 从当前文本选中区中创建 TextRange 对象, //或从控件选中区中创建 controlRange 集合。 var sTextRange= document.selection.createRange(); //判断选中的是不是textarea对象 if(sTextRange.parentElement()== oTextarea){ //创建一个TextRange对象 var oTextRange = document.body.createTextRange(); //移动文本范围以便范围的开始和结束位置能够完全包含给定元素的文本。 oTextRange.moveToElementText(oTextarea); //此时得到两个 TextRange //oTextRange文本域(textarea)中文本的TextRange对象 //sTextRange是选中区域文本的TextRange对象 //compareEndPoints方法介绍,compareEndPoints方法用于比较两个TextRange对象的位置 //StartToEnd 比较TextRange开头与参数TextRange的末尾。 //StartToStart比较TextRange开头与参数TextRange的开头。 //EndToStart 比较TextRange末尾与参数TextRange的开头。 //EndToEnd 比较TextRange末尾与参数TextRange的末尾。 //moveStart方法介绍,更改范围的开始位置 //character 按字符移动 //word 按单词移动 //sentence 按句子移动 //textedit 启动编辑动作 //这里我们比较oTextRange和sTextRange的开头,的到选中区域的开头位置 for (start=0; oTextRange.compareEndPoints("StartToStart", sTextRange) =LEnd && i>=0; i--){ var c = value.charAt(i); if(c!='\n'){ end++; } } oTextRange.moveStart('character', start); oTextRange.moveEnd('character', -end); //oTextRange.collapse(true); oTextRange.select(); oTextarea.focus(); }else{ oTextarea.select(); oTextarea.selectionStart=start; oTextarea.selectionEnd=end; } } start: end: 虞美人 春花秋月何时了,往事知多少。 小楼昨夜又东风,故国不堪回首月明中! 雕l栏玉砌应犹在,只是朱颜改。 问君能有几多愁?恰似一江春水向东流。
串口通信的OVERLAP模式到底是怎么用的?具体过程是什么?
SetCommMask 设置要监控的事件,WaitCommEvent 等待串口通信事件的发生.
SetCommMask
用途:设置串口通信事件 原型:BOOL SetCommMask(HANDLE hFile, //标识通信端口的句柄 DWORD dwEvtMask //能够使能的通信事件 ); 参数说明:-hFile:串口句柄 -dwEvtMask:准备监视的串口事件掩码 串口上可能发生的事件如下表所示:
值 事件描述
EV_BREAK A break was detected on input.
EV_CTS The CTS (clear-to-send) signal changed state.
EV_DSR The DSR(data-set-ready) signal changed state.
EV_ERR A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY.
EV_RING A ring indicator was detected.
EV_RLSD The RLSD (receive-line-signal-detect) signal changed state.
EV_RXCHAR A character was received and placed in the input buffer.
EV_RXFLAG The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function.
EV_TXEMPTY The last character in the output buffer was sent.
参数含义: EV_BREAK:收到BREAK信号。 EV_CTS:CTS(clear to send)线路发生变化。 EV_DSR:DST(Data Set Ready)线路发生变化。 EV_ERR:线路状态错误,包括了CE_FRAME / CE_OVERRUN / CE_RXPARITY 3种错误。 EV_RING:检测到振铃信号。 EV_RLSD:CD(Carrier Detect)线路信号发生变化。 EV_RXCHAR:输入缓冲区中已收到数据,即接收到一个字节并放入输入缓冲区。 EV_RXFLAG:使用SetCommState()函数设置的DCB结构中的等待字符已被传入输入缓冲区中。 EV_TXEMPTY:输出缓冲区中的数据已被完全送出。
操作举例:SetCommMask(hComm,EV_RXCHAR|EV_TXEMPTY);
上面函数执行完毕后将监视串口中有无数据和发送缓冲区中的数据是否全部发送完毕。
WaitCommEvent()
用途:用来判断用SetCommMask()函数设置的串口通信事件是否已发生。
原型:BOOL WaitCommEvent(HANDLE hFile,
LPDWORD lpEvtMask,
LPOVERLAPPED lpOverlapped
);
参数说明:
-hFile:串口句柄
-lpEvtMask:函数执行完后如果检测到串口通信事件的话就将其写入该参数中。
-lpOverlapped:异步结构,用来保存异步操作结果。
操作举例:OVERLAPPED os;
DWORD dwMask,dwTrans,dwError=0,err;
memset(&os,0,sizeof(OVERLAPPED));
os.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!WaitCommEvent(hComm,&dwMask,&os)){
//如果异步操作不能立即完成的话,函数返回FALSE,并且调用GetLastError()函
//数分析错误原因后返回ERROR_IO_PENDING,指示异步操作正在后台进行.这种情
//况下,在函数返回之前系统设置OVERLAPPED结构中的事件为无信号状态,该函数
//等待用SetCommMask()函数设置的串口事件发生,共有9种事件可被监视:
//EV_BREAK,EV_CTS,EV_DSR,EV_ERR,EV_RING,EV_RLSD,EV_RXCHAR,
//EV_RXFLAG,EV_TXEMPTY;当其中一个事件发生或错误发生时,函数将
//OVERLAPPED结构中的事件置为有信号状态,并将事件掩码填充到dwMask参数中
if(GetLastError()==ERROR_IO_PENDING){
/**************************************************************/
/*在此等待异步操作结果,直到异步操作结束时才返回.实际上此时 */
/*WaitCommEvent()函数一直在等待串口监控的事件之一发生,当事件发*/
/*生时该函数将OVERLAPPED结构中的事件句柄置为有信号状态,此时 */
/*GetOverlappedResult()函数发现此事件有信号后马上返回,然后下面*/
/*的程序马上分析WaitCommEvent()函数等到的事件是被监视的串口事 */
/*件中的哪一个,然后执行相应的动作并发出相应消息. */
/**************************************************************/
GetOverlappedResult(hComm,&os,&dwTrans,true);
switch(dwMask){
case EV_RXCHAR:
PostMessage(Parent,WM_COMM_RXCHAR,0,0);
break;
case EV_TXEMPTY:
PostMessage(Parent,WM_COMM_TXEMPTY,0,0);
break;
case EV_ERR:
switch(dwError){
case CE_FRAME:
err=0;
break;
case CE_OVERRUN:
err=1;
break;
case CE_RXPARITY:
err=2;
break;
default:break;
}
PostMessage(Parent,WM_COMM_ERR,(WPARAM)0,(LPARAM)err);
break;
case EV_BREAK:
PostMessage(Parent,WM_COMM_BREAK,0,0);
break;
case ...://其他用SetCommMask()函数设置的被监视的串口通信事件。
... ...
break;
default:break;
}
}
-以上简要介绍了大部分的串口通信api函数,笔者所写的串口通信软件用的是事件通知方式,该方式是
windows2000下效率较高的一种方式。而且只熟悉这些api函数也还是不够的,该机制下还要牵涉到多
线程和消息机制,其中读写串口的动作是由主线程来完成的,比如说操作者按下发送数据的按钮之后
,相应函数马上将某特定区域里面的数据发送出去,所以说用api函数写串口发送数据的功能是相对较
简单的。收数据的时候就要麻烦一点,在打开串口后首先主线程要设置要监视的串口通信事件,然后
将监视线程打开,用来监视主线程设置的这些串口通信事件是否已发生,当其中的某个事件发生后,
监视线程马上将该消息发送给主线程,其中监视线程在发送消息之前要确保主线程在收到消息后肯定
的知道串口究竟发生了什么样的事件,然后根据不同的事件类型进行处理。
大致的主线程和
监视线程的大致工作流程:
主线程打开(其实就是主窗体打开之后)
|
|
V
打开串口(设置波特率、校验方式、数据位数、停止位数)
|
|
V
设置监视线程需要监视的串口通信事件
|
|
V
打开监视线程
|
|
V
等待各种事件的发生(比如发送数据单击事件,更改通信参数事件,监视线程发来的消息等)
--------------------------------------------------------------------------------
监视线程被打开
|
|
V
串口事件发生否(WaitCommEvent())(无论发生否均进入下面的代码)
|
|
V
异步操作是否正在后台进行?(if(GetLastError()==ERROR_IO_PENDING))
|
|
V
在此等待异步操作结果(GetOverlappedResult(hComm,&os,&dwTrans,true))
|
|
V
处理通信事件,根据事件类型的不同给主窗体发送不同的消息
总结起来说SetCommMask()和WaitCommEvent()是要成对使用的。在串口打开的时候SetCommMask,然后在读数据的线程中WaitCommEvent。不过要注意的是,一般不会在WaitCommEvent之后做关于该串口数据的处理(尤其是在数据比较多,处理缓慢的时候——容易丢失数据吧~~),一般会把数据简单的保存下来,再发送消息给一个专门的处理函数进行处理。
如何:创建和移除自定义事件日志
如果想以比组件向默认 Application 日志写入条目时所允许的方式更精细的方式来组织条目,可创建一个自定义日志。例如,假定您有一个称为 OrderEntry 的组件,它向一个事件日志写入条目信息。较之于 Application 日志中其他一些条目,您希望让这些条目能备份和保存较长一段时间。不是将组件注册为向 Application 日志写入项,而是创建一个名为 OrdersLog 的自定义日志,并将组件注册为向此日志中写入项。这样,您所有的订单信息就存储在一个位置,而且如果 Application 日志中的条目被清除,此信息也不会受到影响。 可以间接使用 CreateEventSource 方法创建自定义日志。此方法可创建新源,并可以让您指定要写入的日志。如果您指定想向一个尚未存在的日志中写入,系统会自动创建一个自定义日志,并将您的组件注册为此日志的一个源。注意移除自定义日志的方法与移除任何日志的方法相同,都是调用 Delete 方法。有关更多信息,请参见如何:删除事件日志。要注意创建事件日志和创建 EventLog 组件的实例之间的区别,这一点很重要。使用 CreateEventSource 方法时,将在 Windows 中创建一个新的自定义事件日志,而不是在您的项目或应用程序中创建一个组件。创建 EventLog 组件的实例时,将在项目内部创建一个组件,该组件引用一个外部事件日志。可以在“事件查看器”中查看用 CreateEventSource 方法创建的事件日志,但不能在其中查看组件实例。 注意通常在安装应用程序的过程中创建新的事件源。这样,操作系统就有时间刷新自己的已注册事件源的列表及其配置。如果操作系统未刷新其事件源列表,而您试图用新的事件源来编写事件,则写操作将失败。如果不能选择在安装过程中创建事件源,则请尝试在第一次写操作之前尽早创建事件源,比如在应用程序初始化过程中创建。如果选择这种方法,请确保使用计算机上的管理员权限运行初始化代码。这些权限是创建新事件源所必需的权限。可以在“服务器资源管理器”中或在 Windows 2000 的“计算机管理”窗口中查看自定义日志。有关更多信息,请参见如何:在服务器资源管理器中使用事件日志或如何:启动事件查看器。必须在远程计算机上具有适当的访问权限才能创建和删除日志。有关更多信息,请参见事件日志的安全性细节。安全注意在创建事件日志时,请注意资源可能已经存在。另一进程(可能是恶意进程)可能已创建了资源,并拥有对该资源的访问权。将数据放入事件日志后,其他进程就可使用这些数据了。有关现有事件日志的信息,请参见如何:确定特定事件日志是否存在。创建自定义事件日志调用CreateEventSource 方法,并指定源字符串和要创建的日志文件的名称。 注意如果将日志名称指定为空 (""),则将默认使用 Application 日志。这样不会创建新日志,但会为 Application 日志注册指定的源。如果创建一个新的日志,则在确定名称是否唯一时将只计算前 8 个字母。 下面的示例演示如何在本地计算机上创建一个名为 MyNewLog 的自定义日志。此代码假设对应 System.Diagnostics 命名空间存在一个 Imports 或using 语句: C#VB System.Diagnostics.EventLog.CreateEventSource("ApplicationName", "LogName"); J# System.Diagnostics.EventLog.CreateEventSource("ApplicationName", "LogName"); 若要在远程计算机上创建自定义事件日志,请将此计算机名指定为第三个参数。下面的代码提供了一个示例。 C#VB System.Diagnostics.EventSourceCreationData creationData = new System.Diagnostics.EventSourceCreationData("ApplicationName", "LogName"); creationData.MachineName = "ServerName"; EventLog.CreateEventSource(creationData); J# System.Diagnostics.EventSourceCreationData creationData = new System.Diagnostics.EventSourceCreationData("ApplicationName", "LogName"); creationData.set_MachineName = "ServerName"; EventLog.CreateEventSource(creationData); 请参见任务如何:删除事件日志如何:在服务器资源管理器中使用事件日志如何:启动事件查看器参考EventLog概念事件日志的安全性细节EventLog 组件介绍其他资源管理事件日志
如何建立程序日志系统
在写入日志条目时,系统使用您标识的源来查找适当的日志以放入您的条目。EventLog 组件实例一次只能向一个日志中写入项。说明:默认情况下,如果事先未将组件注册为有效的源就尝试写入日志项,系统会自动将该源注册到事件日志中,并将 Source 属性的值用作源字符串。通常在安装应用程序的过程中创建新的事件源。这样,操作系统就有时间刷新自己的已注册事件源的列表及其配置。如果操作系统未刷新其事件源列表,而您试图用新的事件源来编写事件,则写操作将失败。如果不能选择在安装过程中创建事件源,则请尝试在第一次写操作之前尽早创建事件源,比如在应用程序初始化过程中创建。如果选择这种方法,请确保使用计算机上的管理员权限运行初始化代码。这些权限是创建新事件源所必需的权限。如果在使用 CreateEventSource 方法时指定了并不存在的日志名,系统将在您第一次尝试向日志中写入项时使用该名称创建一个新的、自定义的事件日志。有关更多信息,请参见 如何:创建和移除自定义事件日志。可以使用事件类别和消息字符串的本地化资源来注册事件源。应用程序可以使用资源标识符而不是通过指定实际字符串值来写入事件日志项。以下代码提供了一个示例:C#VB System.Diagnostics.EventSourceCreationData creationData = new System.Diagnostics.EventSourceCreationData("ApplicationName", "Application"); creationData.MachineName = "ServerName"; EventLog.CreateEventSource(creationData); 请参见任务如何:确定事件源是否存在如何:移除事件源如何:创建和移除自定义事件日志演练:浏览事件日志、事件源和项概念EventLog 组件介绍参考EventLog
怎样用js模拟键盘事件
主动触发事件是一个经常会用到的技巧,它很重要。它和事件绑定一样存在着浏览器兼容性问题。传统浏览器(IE8-)使用createEventObject来创建事件对象,用fireEvent这个方法来触发事件;现代浏览器用createEvent来创建对象,用dspatchEvent来触发事件。
//获取浏览器版本
var isIE=navigator.userAgent.match(/MSIE (;d)/i);
isIE=isIE?isIE[1]:undefined;
//事件函数
function onclick(e){
e=eevent;
alert(e.msg表酱紫啦,人家羞涩啦~);
};
//给document绑定一个点击事件
isIElt;9
//传统浏览器使用attachEvent
?document.attachEvent(onclick,onclick)
//现代浏览器使用addEventListner
:document.addEventListener(click,onclick,false);
//触发自定义事件
if(isIElt;9){
//传统浏览器
//创建对象
var event=document.createEventObject();
//给事件对象添加属性
event.msg=我是fireEvent触发的;
//触发事件
document.fireEvent(onclick,event);
}else{
//现代浏览器
//创建事件对象
var e=document.createEvent(MouseEvents);
//初始化事件对象
e.initMouseEvent(click),
//给事件对象添加属性
e.msg=我是despatchEvent触发的;
//触发事件
document.dispatchEvent(e);
};