Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Custom Events | Interactive GUI
Advanced C# with .NET
course content

Course Content

Advanced C# with .NET

Advanced C# with .NET

1. Introduction to Desktop Development with .NET
2. Interactive GUI
3. Threading
4. Generics & Reflection

Custom Events

In the previous chapter, we learnt how to bind methods to event handlers, however, is it possible to create our own Events? Yes, in fact it is also quite simple to do so.

An event system consists of two parts: The Publisher class and the Subscriber class. There is always a single Publisher class for an event, however there can be multiple Subscriber classes.

A Publisher class contains the event handler delegate for the event, and a method to invoke that delegate whenever relevant.

A class is referred to as a Subscriber class in this context if it subscribes any method to the event handler of the Publisher class.

The following diagram shows the process flow of an Event System:

We will create a simple event system starting from the default project. Inside the C# file, we first create a new class called CounterEvent which is going to represent the Publisher class, and it's going to have an EventHandler instance which we create using the already existing EventHandler type:

cs

index

copy
123
public class CounterEvent { public event EventHandler AtTensMultiple; }

This class also needs a method to invoke the event handler whenever necessary. We can create an event which is triggered whenever the condition count % 10 == 0 is satisfied, where count is the number of times the user has clicked the CounterBtn. Let's first define the invoke method inside the CounterEvent, we will call is CountCheck because it will basically check the value of count every time it's called and it will invoke AtTensMultiple when the condition is satisfied.

cs

index

copy
12345678910111213
public class CounterEvent { public event EventHandler? AtTensMultiple; public void CountCheck(int count) { if(count % 10 == 0) { EventHandler? handler = AtTensMultiple; if(handler) { AtTensMultiple(this, new EventArgs()); } } } }

Here this argument is being passed to represent the sender and new EventArgs() represents a new instance of an empty class. We can pass data into the methods that have subscribed to the event handler, when the event handler is invoked, using this second argument, however since we are passing EventArgs which is an empty class, no data is passed. We will see how to pass data this way in a bit.

Now that the invoke method is defined, our publisher class is pretty much complete. We need to do two more things:

  1. Create an instance of the publisher class;
  2. Call the invoke method to make it functional;

So, first I will create an instance inside the MainPage class:

cs

index

copy
1
public static CounterEvent counter = new CounterEvent();

And now, let's call this method inside OnCounterClicked so that it runs a check every time the user clicks the button.

cs

index

copy
12345678910
private void OnCounterClicked(object? sender, new EventArgs e) { count++; if(count == 0) CounterBtn.Text = $"Clicked {count} time"; else CounterBtn.Text = $"Clicked {count} times"; counter.CounterCheck(count); }

Now that our event is functional, we are ready to bind a method to the event handler. I will create a new method called Reformat which simply appends (Ten's Multiple) text to the end of the button text whenever the method is invoked. Our complete code will look something like this by now:

cs

index

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
using System.Runtime.CompilerServices; namespace EventsIntro { public class CounterEvent { public event EventHandler? AtTensMultiple; public void CountCheck(int count) { EventHandler? handler = AtTensMultiple; if (handler != null) { if (count % 10 == 0) { handler(this, new EventArgs()); } } } } public partial class MainPage : ContentPage { int count = 0; CounterEvent counter = new CounterEvent(); public MainPage() { InitializeComponent(); CounterBtn.Clicked += OnCounterClicked; counter.AtTensMultiple += Reformat; } private void Reformat(object? sender, EventArgs e) { CounterBtn.Text += " " + "(Ten's Multiple)"; } private void OnCounterClicked(object? sender, EventArgs e) { count++; if (count == 1) CounterBtn.Text = $"Clicked {count} time"; else CounterBtn.Text = $"Clicked {count} times"; counter.CountCheck(count); } } }

So now if we run the program and click the button 10 times, the text (Ten's Multiple) will appear in the button text. If you press it 20 times, the same will happen again. And so on.

Now, to pass arguments into the Reformat method, we can create a new class called CustomEventArgs derived from EventArgs, and can have some public properties in that class which can represent the parameters:

cs

index

copy
123456789
public class CounterArgs : EventArgs { public string Text { get; } // Here 'Text' can act as a parameter or argument. public CounterArgs(string text) { this.Text = text; } }

Now that we have a CustomEvenArgs class, we need make a few changes to the CounterEvent class:

cs

index

copy
12345678910111213141516171819
public class CounterEvent { public delegate void CounterEventHandler(object sender, CounterArgs e); public event CounterEventHandler? AtTensMultiple; public void CountCheck(int count) { CounterEventHandler? handler = AtTensMultiple; if (handler != null) { if (count % 10 == 0) { handler(this, new CounterArgs("(something random)")); } } } }

In the code above, we first create a new definition for the EventHandler and call it CustomEventHandler, where we set the datatype of the second parameter to CustomEventArgs.

Secondly, we pass new CounterArgs("something random") as a second argument when invoking the event handler. This lets us mass the string data (something random) as an argument to the Refactor method, which we can access like this:

cs

index

copy
12345
// Modified signature to support 'CounterArgs' instead of 'EventArgs' private void Reformat(object? sender, CounterArgs e) { CounterBtn.Text += " " + e.Text; }

So, now, our final code should look something like this:

cs

index

copy
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
using System.Runtime.CompilerServices; namespace EventsIntro { public class CounterEvent { public delegate void CustomEventHandler(object? sender, CounterArgs e); public event CustomEventHandler? AtTensMultiple; public void CountCheck(int count) { CustomEventHandler? handler = AtTensMultiple; if (handler != null) { if (count % 10 == 0) { handler(this, new CounterArgs("(something random)")); } } } } public class CounterArgs : EventArgs { public string Text { get; } public CounterArgs(string text) { this.Text = text; } } public partial class MainPage : ContentPage { int count = 0; CounterEvent counter = new CounterEvent(); public MainPage() { InitializeComponent(); CounterBtn.Clicked += OnCounterClicked; counter.AtTensMultiple += Reformat; } private void Reformat(object? sender, CounterArgs e) { CounterBtn.Text += " " + e.Text; } private void OnCounterClicked(object? sender, EventArgs e) { count++; if (count == 1) CounterBtn.Text = $"Clicked {count} time"; else CounterBtn.Text = $"Clicked {count} times"; counter.CountCheck(count); } } }

If you run the above program, the text (something random) will be appended to the end of the button text whenever the count is a multiple of ten. Which indicates that the data is successfully being passed into the subscriber methods.

Important Points:

  • The Publisher Class contains the definition and an instance of the custom event handler delegate;
  • The can skip the definition of the Event Handler Delegate if we are using EventHandler delegate type to create the instance;
  • There is a method in the Publisher Class which is responsible for invoking the delegate whenever it's relevant;
  • The Delegate Instance should be public to make it accessible for subscriber classes;
  • We can pass arguments into Event Handler Methods by creating our own class which derives from EventArgs and having properties that represent the data or arguments which we want to pass;
1. Which keyword do we use for converting a normal delegate into an event handler?
2. Which class do we need to derive from when creating custom event method arguments?

Which keyword do we use for converting a normal delegate into an event handler?

Select the correct answer

Which class do we need to derive from when creating custom event method arguments?

Select the correct answer

Everything was clear?

Section 2. Chapter 5
We're sorry to hear that something went wrong. What happened?
some-alt