C_Sharp_Language

C Sharp TutorialC Sharp .NET Compiler Platform (Roslyn)C Sharp 3.0 FeaturesC Sharp 4.0 FeaturesC Sharp 5.0 FeaturesC Sharp 6.0 FeaturesC Sharp 7.0 FeaturesC Sharp Access ModifiersC Sharp Access network shared folder with username and passwordC Sharp Accessing DatabasesC Sharp Action FiltersC Sharp Aliases of built-in typesC Sharp an overview of collectionsC Sharp Anonymous typesC Sharp ArraysC Sharp ASP.NET IdentityC Sharp AssemblyInfo.cs ExamplesC Sharp Async-AwaitC Sharp Async/await, Backgroundworker, Task and Thread ExamplesC Sharp Asynchronous SocketC Sharp AttributesC Sharp Authentication handlerC Sharp BackgroundWorkerC Sharp BigIntegerC Sharp Binary SerializationC Sharp BindingListC Sharp Built-in TypesC Sharp CachingC Sharp CastingC Sharp Checked and UncheckedC Sharp CLSCompliantAttributeC Sharp Code ContractsC Sharp Code Contracts and AssertionsC Sharp Collection InitializersC Sharp Comments and regionsC Sharp Common String OperationsC Sharp Conditional StatementsC Sharp Constructors and FinalizersC Sharp Creating Own MessageBox in Windows Form ApplicationC Sharp Creational Design PatternsC Sharp Cryptography (System.Security.Cryptography)C Sharp Data AnnotationC Sharp DateTime MethodsC Sharp DelegatesC Sharp Dependency InjectionC Sharp DiagnosticsC Sharp Dynamic typeC Sharp EnumC Sharp Equality OperatorC Sharp Equals and GetHashCodeC Sharp EventsC Sharp Exception HandlingC Sharp Expression TreesC Sharp Extension MethodsC Sharp File and Stream I/OC Sharp FileSystemWatcherC Sharp Func delegatesC Sharp Function with multiple return valuesC Sharp Functional ProgrammingC Sharp Garbage Collector in .NETC Sharp Generating Random NumbersC Sharp Generic Lambda Query BuilderC Sharp GenericsC Sharp Getting Started: Json with C SharpC Sharp GuidC Sharp Handling FormatException when converting string to other typesC Sharp Hash FunctionsC Sharp ICloneableC Sharp IComparableC Sharp IDisposable interfaceC Sharp IEnumerableC Sharp ILGeneratorC Sharp ImmutabilityC Sharp Implementing Decorator Design PatternC Sharp Implementing Flyweight Design PatternC Sharp Import Google ContactsC Sharp Including Font ResourcesC Sharp IndexerC Sharp InheritanceC Sharp Initializing PropertiesC Sharp INotifyPropertyChanged interfaceC Sharp InterfacesC Sharp InteroperabilityC Sharp IQueryable interfaceC Sharp IteratorsC Sharp KeywordsC Sharp Lambda expressionsC Sharp Lambda ExpressionsC Sharp LINQ QueriesC Sharp LINQ to Objects



C Sharp Events

From WikiOD

An event is a notification that something has occurred (such as a mouse click) or, in some cases, is about to occur (such as a price change).

Classes can define events and their instances (objects) may raise these events. For instance, a Button may contain a 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.

Parameters[edit | edit source]

Parameter Details
EventArgsT The type that derives from EventArgs and contains the event parameters.
EventName The name of the event.
HandlerName The name of the event handler.
SenderObject The object that's invoking the event.
EventArguments An instance of the EventArgsT type that contains the event parameters.

Remarks[edit | edit source]

When raising an event:

  • Always check if the delegate is null. A null delegate means the event has no subscribers. Raising an event with no subscribers will result in a NullReferenceException.

6.0

  • Copy the delegate (e.g. EventName) to a local variable (e.g. eventName) before checking for null / raising the event. This avoids race conditions in multi-threaded environments:

Wrong:

    if(Changed != null)      // Changed has 1 subscriber at this point
                             // In another thread, that one subscriber decided to unsubscribe
        Changed(this, args); // `Changed` is now null, `NullReferenceException` is thrown.

Right:

    // Cache the "Changed" event as a local. If it is not null, then use
    // the LOCAL variable (handler) to raise the event, NOT the event itself.
    var handler = Changed;
    if(handler != null)
        handler(this, args);

6.0

  • Use the null-conditional operator (?.) for raising the method instead of null-checking the delegate for subscribers in an if statement: EventName?.Invoke(SenderObject, new EventArgsT());
  • When using Action<> to declare delegate types, the anonymous method / event handler signature must be the same as the declared anonymous delegate type in the event declaration.

Declaring and Raising Events[edit | edit source]

Declaring an Event[edit | edit source]

You can declare an event on any class or struct using the following syntax:

public class MyClass
{
    // Declares the event for MyClass
    public event EventHandler MyEvent;

    // Raises the MyEvent event
    public void RaiseEvent()
    {
        OnMyEvent();
    }
}

There is an expanded syntax for declaring events, where you hold a private instance of the event, and define a public instance using add and set accessors. The syntax is very similar to C# properties. In all cases, the syntax demonstrated above should be preferred, because the compiler emits code to help ensure that multiple threads can safely add and remove event handlers to the event on your class.

Raising the Event[edit | edit source]

6.0

private void OnMyEvent()
{
    EventName?.Invoke(this, EventArgs.Empty); 
}

6.0

private void OnMyEvent()
{
    // Use a local for EventName, because another thread can modify the
    // public EventName between when we check it for null, and when we
    // raise the event.
    var eventName = EventName;

    // If eventName == null, then it means there are no event-subscribers,
    // and therefore, we cannot raise the event.
    if(eventName != null)
        eventName(this, EventArgs.Empty);

}

Note that events can only be raised by the declaring type. Clients can only subscribe/unsubscribe.

For C# versions before 6.0, where EventName?.Invoke is 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 multiple threads execute the same code. Failing to do so may cause a NullReferenceException to be thrown in 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.

Creating cancelable event[edit | edit source]

A cancelable event can be raised by a class when it is about to perform an action that can be canceled, such as the FormClosing event of a Form.

To create such event:

  • Create a new event arg deriving from CancelEventArgs and add additional properties for event data.
  • Create an event using EventHandler<T> and use the new cancel event arg class which you created.

Example

In the below example, we create a PriceChangingEventArgs event for Price property of a class. The event data class contains a Value which let the consumer know about the new . The event raises when you assign a new value to Price property 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 Price will be used:

PriceChangingEventArgs

public class PriceChangingEventArgs : CancelEventArgs
{
    int value;
    public int Value
    {
        get { return value; }
    }
    public PriceChangingEventArgs(int value)
    {
        this.value = value;
    }
}

Product

public class Product
{
    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(value);
            OnPriceChanging(e);
            if (!e.Cancel)
                price = value;
        }
    }

    public event EventHandler<PriceChangingEventArgs> PropertyChanging;
    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PropertyChanging;
        if (handler != null)
            PropertyChanging(this, e);
    }
}

Event Properties[edit | edit source]

If a class raises a large the number of events, the storage cost of one field per delegate may not be acceptable. The .NET Framework provides event properties for these cases. This way you can use another data structure like EventHandlerList to store event delegates:

public class SampleClass 
{
    // Define the delegate collection.
    protected EventHandlerList eventDelegates = new EventHandlerList();

    // Define a unique key for each event.
    static readonly object someEventKey = new object();

    // Define the SomeEvent event property.
    public event EventHandler SomeEvent
    {
        add
        {
            // Add the input delegate to the collection.
            eventDelegates.AddHandler(someEventKey, value);
        }
        remove
        {
            // Remove the input delegate from the collection.
            eventDelegates.RemoveHandler(someEventKey, value);
        }
    }

    // Raise the event with the delegate specified by someEventKey
    protected void OnSomeEvent(EventArgs e)
    {
        var handler = (EventHandler)eventDelegates[someEventKey];
        if (handler != null)
            handler(this, e);
    }
}

This approach is widely used in GUI frameworks like WinForms where controls can have dozens and even hundreds of events.

Note that EventHandlerList is not thread-safe, so if you expect your class to be used from multiple threads, you will need to add lock statements or other synchronization mechanism (or use a storage that provides thread safety).

Standard Event Declaration[edit | edit source]

Event declaration:

public event EventHandler<EventArgsT> EventName;

Event handler declaration:

public void HandlerName(object sender, EventArgsT args) { /* Handler logic */ }

Subscribing to the event:

Dynamically:

EventName += HandlerName;

Through the Designer:

  1. Click the Events button on the control's properties window (Lightening bolt)
  2. Double-click the Event name:

Events

  • Visual Studio will generate the event code:
private void Form1_Load(object sender, EventArgs e)
{

}

Invoking the method:

EventName(SenderObject, EventArguments);

Anonymous Event Handler Declaration[edit | edit source]

Event declaration:

public event EventHandler<EventArgsType> EventName;

Event handler declaration using lambda operator => and subscribing to the event:

EventName += (obj, eventArgs) => { /* Handler logic */ };

Event handler declaration using delegate anonymous method syntax:

EventName += delegate(object obj, EventArgsType eventArgs) { /* Handler Logic */ };

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 { /* Handler Logic */ }

Invoking the event:

EventName?.Invoke(SenderObject, EventArguments);

Non-Standard Event Declaration[edit | edit source]

Events can be of any delegate type, not just EventHandler and EventHandler<T>. For example:

//Declaring an event
public event Action<Param1Type, Param2Type, ...> EventName;

This is used similarly to standard EventHandler events:

//Adding a named event handler
public void HandlerName(Param1Type parameter1, Param2Type parameter2, ...) {
    /* Handler logic */
}
EventName += HandlerName;

//Adding an anonymous event handler
EventName += (parameter1, parameter2, ...) => { /* Handler Logic */ };

//Invoking the event
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):

public event EventHandler Event1, Event2, Event3;

This declares three separate events (Event1, Event2, and Event3) all of type EventHandler.

Note: Although some compilers may accept this syntax in interfaces as well as classes, the C# 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.

Creating custom EventArgs containing additional data[edit | edit source]

Custom events usually need custom event arguments containing information about the event. For example MouseEventArgs which is used by mouse events like MouseDown or MouseUp events, contains information about Location or Buttons which used to generate the event.

When creating new events, to create a custom event arg:

  • Create a class deriving from EventArgs and define properties for necessary data.
  • As a convention, the name of the class should ends with EventArgs.

Example

In the below example, we create a PriceChangingEventArgs event for Price property of a class. The event data class contains a CurrentPrice and a NewPrice. The event raises when you assign a new value to Price property and lets the consumer know the value is changing and let them to know about current price and new price:

PriceChangingEventArgs

public class PriceChangingEventArgs : EventArgs
{
    public PriceChangingEventArgs(int currentPrice, int newPrice)
    {
        this.CurrentPrice = currentPrice;
        this.NewPrice = newPrice;
    }

    public int CurrentPrice { get; private set; }
    public int NewPrice { get; private set; }
}

Product

public class Product
{
    public event EventHandler<PriceChangingEventArgs> PriceChanging;

    int price;
    public int Price
    {
        get { return price; }
        set
        {
            var e = new PriceChangingEventArgs(price, value);
            OnPriceChanging(e);
            price = value;
        }
    }

    protected void OnPriceChanging(PriceChangingEventArgs e)
    {
        var handler = PriceChanging;
        if (handler != null)
            handler(this, e);
    }
}

You can enhance the example by allowing the consumer to change the new value and then the value will be used for property. To do so it's enough to apply these changes in classes.

Change the definition of NewPrice to be settable:

public int NewPrice { get; set; }

Change the definition of Price to use e.NewPrice as value of property, after calling OnPriceChanging :

int price;
public int Price
{
    get { return price; }
    set
    {
        var e = new PriceChangingEventArgs(price, value);
        OnPriceChanging(e);
        price = e.NewPrice;
    }
}

Credit:Stack_Overflow_Documentation