Chapter89:Events

No Comments

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.

Section89.2:EventProperties

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:

  1. Click theEvents buttonon thecontrol’s propertieswindow (Lighteningbolt)
  2. 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 posts
No Comments

Recent Posts

Leave a Comment