Parameter Details
EventArgsT ThetypethatderivesfromEventArgsandcontainstheeventparameters.
EventName The name of the event.
HandlerName The name of the event handler.
SenderObject Theobjectthat’sinvokingtheevent.
EventArgumentsAninstanceoftheEventArgsTtypethatcontainstheeventparameters.
Aneventisanotificationthatsomethinghasoccurred(suchasamouseclick)or,insomecases,isabouttooccur (such as a price change).
Classescandefineeventsandtheirinstances(objects)mayraisetheseevents.Forinstance,aButtonmaycontaina Click event that gets raised when a user has clicked it.
Event handlers are then methods that get called when their corresponding event is raised. A form may contain a Clicked event handler for every Button it contains, for instance.
Section89.1:DeclaringandRaisingEvents
DeclaringanEvent
Youcandeclareaneventonanyclassorstructusingthefollowingsyntax:
publicclassMyClass
{
//DeclarestheeventforMyClass
publiceventEventHandlerMyEvent;
//RaisestheMyEventevent
publicvoidRaiseEvent()
{
OnMyEvent();
}
}
Thereisanexpandedsyntaxfordeclaringevents,whereyouholdaprivateinstanceoftheevent,anddefinea publicinstanceusingaddandsetaccessors.ThesyntaxisverysimilartoC#properties.Inallcases,thesyntax demonstratedaboveshouldbepreferred,becausethecompileremitscodetohelpensurethatmultiplethreads cansafelyaddandremoveeventhandlerstotheeventonyourclass.
RaisingtheEvent
Version ≥ 6.0
privatevoidOnMyEvent()
{
EventName?.Invoke(this,EventArgs.Empty);
}
Version <6.0
privatevoidOnMyEvent()
{
//UsealocalforEventName,becauseanotherthreadcanmodifythe
//publicEventNamebetweenwhenwecheckitfornull,andwhenwe
//raisetheevent.
vareventName=EventName;
//IfeventName==null,thenitmeanstherearenoevent-subscribers,
//andtherefore,wecannotraisetheevent.
if(eventName!=null)eventName(this,
EventArgs.Empty);
}
Notethateventscanonlyberaisedbythedeclaringtype.Clientscanonlysubscribe/unsubscribe.
For C# versions before 6.0, where EventName?.Invokeis not supported, it is a good practice to assign the event to a temporary variable before invocation, as shown in the example, which ensures thread-safety in cases where multiplethreadsexecutethesamecode.FailingtodosomaycauseaNullReferenceExceptiontobethrownin certain cases where multiple threads are using the same object instance. In C# 6.0, the compiler emits code similar to that shown in the code example for C# 6.
Ifaclassraisesalargethenumberofevents,thestoragecostofonefieldperdelegatemaynotbeacceptable. The
.NETFrameworkprovideseventpropertiesforthesecases.Thiswayyoucanuseanotherdatastructurelike
EventHandlerListtostoreeventdelegates:
publicclassSampleClass
{
//Definethedelegatecollection.
protectedEventHandlerListeventDelegates=newEventHandlerList();
//Defineauniquekeyforeachevent.
staticreadonlyobjectsomeEventKey=newobject();
//DefinetheSomeEventeventproperty.
publiceventEventHandlerSomeEvent
{
add
{
//Addtheinputdelegatetothecollection.
eventDelegates.AddHandler(someEventKey,value);
}
remove
{
//Removetheinputdelegatefromthecollection.
eventDelegates.RemoveHandler(someEventKey,value);
}
}
//RaisetheeventwiththedelegatespecifiedbysomeEventKey
protectedvoidOnSomeEvent(EventArgse)
{
varhandler=(EventHandler)eventDelegates[someEventKey];
if(handler!=null)
handler(this,e);
}
}
ThisapproachiswidelyusedinGUIframeworkslikeWinFormswherecontrolscanhavedozensandevenhundreds of events.
Note that EventHandlerListis not thread-safe, so if you expect your class to be used from multiple threads, youwill need to add lock statements or other synchronization mechanism (or use a storage that provides thread
safety).
Section89.3:Creatingcancelableevent
Acancelableeventcanberaisedbyaclasswhenitisabouttoperformanactionthatcanbecanceled,suchasthe
FormClosingevent of a Form. To create such event:
- CreateaneweventargderivingfromCancelEventArgsandaddadditionalpropertiesforeventdata.
- Create an event using EventHandler<T>and use the new cancel event arg class which you created.
Example
Inthebelowexample,wecreateaPriceChangingEventArgseventforPricepropertyofaclass.Theeventdata classcontainsaValuewhichlettheconsumerknowaboutthenew.Theeventraiseswhenyouassignanewvalue to Priceproperty and lets the consumer know the value is changing and let them to cancel the event. If the consumer cancels the event, the previous value for Pricewill be used:
PriceChangingEventArgs
publicclassPriceChangingEventArgs:CancelEventArgs
{
intvalue;publicin
tValue
{
get{returnvalue;}
}
publicPriceChangingEventArgs(intvalue)
{
this.value=value;
}
}
Product
publicclassProduct
{
intprice;
publicintPrice
{
get{returnprice;} set
{
vare=newPriceChangingEventArgs(value);OnPriceChanging(e);
if(!e.Cancel)
price=value;
}
}
publiceventEventHandler<PriceChangingEventArgs>PropertyChanging; protectedvoidOnPriceChanging(PriceChangingEventArgse)
{
varhandler=PropertyChanging;
if(handler!=null)
PropertyChanging(this,e);
}
}
Section89.4:StandardEventDeclaration
Eventdeclaration:
publiceventEventHandler<EventArgsT>EventName;
Eventhandlerdeclaration:
publicvoidHandlerName(objectsender,EventArgsTargs){/*Handlerlogic*/}
Subscribingtotheevent:
Dynamically:
EventName+=HandlerName;
ThroughtheDesigner:
- Click theEvents buttonon thecontrol’s propertieswindow (Lighteningbolt)
- Double-clicktheEventname:
3. VisualStudiowillgeneratetheeventcode:
privatevoidForm1_Load(objectsender,EventArgse)
{
}
Invokingthemethod:
EventName(SenderObject,EventArguments);
Section89.5:AnonymousEventHandlerDeclaration
Eventdeclaration:
publiceventEventHandler<EventArgsType>EventName;
Event handlerdeclaration usinglambda operator=>and subscribingto theevent:
EventName+=(obj,eventArgs)=>{/*Handlerlogic*/};
Event handlerdeclaration usingdelegate anonymousmethod syntax:
EventName+=delegate(objectobj,EventArgsTypeeventArgs){/*HandlerLogic*/};
Declaration & subscription of an event handler that does not use the event’s parameter, and so can use the above syntax without needing to specify parameters:
EventName+=delegate{/*HandlerLogic*/}
Invoking theevent:
EventName?.Invoke(SenderObject,EventArguments);
Section89.6:Non-StandardEventDeclaration
Eventscanbeofanydelegatetype,notjustEventHandlerandEventHandler<T>.Forexample:
//Declaringanevent
publiceventAction<Param1Type,Param2Type,…>EventName;
ThisisusedsimilarlytostandardEventHandlerevents:
//Addinganamedeventhandler
publicvoidHandlerName(Param1Typeparameter1,Param2Typeparameter2,…){
/*Handlerlogic*/
}
EventName+=HandlerName;
//Addingananonymouseventhandler
EventName+=(parameter1,parameter2,…)=>{/*HandlerLogic*/};
//Invokingtheevent
EventName(parameter1,parameter2,…);
It is possible to declare multiple events of the same type in a single statement, similar to with fields and local variables (though this may often be a bad idea):
publiceventEventHandlerEvent1,Event2,Event3;
Thisdeclaresthreeseparateevents(Event1,Event2,andEvent3)alloftypeEventHandler.
Note:Althoughsomecompilersmayacceptthissyntaxininterfacesaswellasclasses,theC#specification(v5.0§13.2.3) provides grammar for interfaces that does not allow it, so using this in interfaces may be unreliable with different
compilers.
Section89.7:CreatingcustomEventArgscontaining additional data
Customeventsusuallyneedcustomeventargumentscontaininginformationabouttheevent.Forexample MouseEventArgswhichisusedbymouseeventslikeMouseDownorMouseUpevents,containsinformationabout Locationor Buttonswhich used to generate the event.
Whencreatingnewevents,tocreateacustomeventarg:
Create a class deriving from EventArgsand define properties for necessary data. As a convention, the name of the class should ends with EventArgs.
Example
Inthebelowexample,wecreateaPriceChangingEventArgseventforPricepropertyofaclass.Theeventdata class contains a CurrentPriceand a NewPrice. The event raises when you assign a new value to Priceproperty andlets the consumer know the value is changing and let them to know about current price and new price:
PriceChangingEventArgs
publicclassPriceChangingEventArgs:EventArgs
{
publicPriceChangingEventArgs(intcurrentPrice,intnewPrice)
{
this.CurrentPrice=currentPrice;
this.NewPrice=newPrice;
}
publicintCurrentPrice{get;privateset;}
publicintNewPrice{get;privateset;}
}
Product
publicclassProduct
{
publiceventEventHandler<PriceChangingEventArgs>PriceChanging;
intprice;
publicintPrice
{
get{returnprice;} set
{
vare=newPriceChangingEventArgs(price,value);
OnPriceChanging(e);
price=value;
}
}
protectedvoidOnPriceChanging(PriceChangingEventArgse)
{
varhandler=PriceChanging;
if(handler!=null)
handler(this,e);
}
}
Youcanenhancetheexamplebyallowingtheconsumertochangethenewvalueandthenthevaluewillbeused for property. To do so it’s enough to apply these changes in classes.
ChangethedefinitionofNewPricetobesettable:
publicintNewPrice{get;set;}
ChangethedefinitionofPricetousee.NewPriceasvalueofproperty,aftercallingOnPriceChanging:
intprice;
publicintPrice
{
get{returnprice;} set
{
vare=newPriceChangingEventArgs(price,value);
OnPriceChanging(e);
price=e.NewPrice;
}
}
About us and this blog
We are a digital marketing company with a focus on helping our customers achieve great results across several key areas.
Request a free quote
We offer professional SEO services that help websites increase their organic search score drastically in order to compete for the highest rankings even when it comes to highly competitive keywords.
Subscribe to our newsletter!
More from our blog
See all postsRecent Posts
- Chapter110:Timers 10/05/2024
- Chapter109:Stream 09/05/2024
- Chapter108:CheckedandUnchecked 08/05/2024