Friday, November 19, 2010

[C#] - Custom Events


Introduction to Events


An event is kind of ‘Something Happened’. Some examples are the button got pressed; a checkmark from the check box is removed. We all know that we call this kind of action as Events happening to those controls.

So let us consider a form that has a button in it. And we all know that a button can be clicked. The user does the action of clicking a button and we do not know when that action will happen. In this situation, if we consider the application wants to know when the button got clicked and wants to say “Hi! There”. So what you think now. Somebody should say the button is clicked and somebody should respond back to the event in our case saying just a “Hi”.

I can hear you are saying Not a big deal. Double click the button, the Development environment will bring me to a function, and write the code there that says Hi”. Well. If the Team Lead (Yes, the same guy who always bugs you) asks you “Hey! We have a class called Product Stock and it maintains the stock in hand in an integer variable. Can you expose an event say LowStock so that the interested one will get a notification when the stock in hand goes below five?

What is the answer?  “Yes”? OK, then you already know the Events very well. It is your choice whether you want to refresh it here or not.
If your answer is “Hey. I know how to handle the UI events. I have not created my own event”, then this article is for you. Let us start.

Publish and Subscribe


If we go back to the button click on the form that says “Hi there”, there are some piece of information we need to know.

  1. The button is placed on the form. The form is the container that holds the button, which is a UI Component.
  2. The Button class in dot net exposes an event called Click. So button class publishes the event click.
  3. The Form class wants to know when the button got clicked. So it subscribes for the published event Click.
  4. When the Button is clicked, Subscriber for the event Click on the button is notified. And there is handler code that says Hi when the notification is received.

So the publish is nothing but exposing the event and subscribe is kind of getting the notification on the supplied function. Delegate and event are tightly coupled. We will see how when we are going through an example and at the same time you can answer the question for your team lead.

The Example


In this example, we have two classes. One is the ProductStock class, which maintains the current stock of the product. The other class is Counter used by the billing counter computers in the retail shop. Let us say A customer comes to billing a counter, informs the product he wants to purchase, pays the bill and goes to the stored room to receive the product. Each billing counter receives a notification when the product stock goes low.

Consider the below picture before we move on:




1) The ProductStock class publishes the event LowStock
2) Purchase, Counter etc classes subscribe to the Published event LowStock.
3) ProductStock sends a notification to the entire subscriber when to ProductStock goes low.

In our Example, we are not going to Implement Purchase and Someother class.

The ProductStock Class


1) The ProductStock has two member variables. One to know the product name and another one is keeping track of the current stock. The current stock is get reduced by the sales counter when a sale of the product is performed.

//001_1: Member Variable.
public string ProductName;
private int StockInHand;

2) The class declares a multicast delegate OnStockLow that takes an Event source object and EventArgs object. The event source is ProductStock as it is going to raise the notification (Event). EventArgs is usually a class derived from it EventArgs that can be used to pack the information related to the event. To keep this example simple I have not derived any object from EventArgs. Below is the declaration of Multicast delegate:

//001_2: Multicast delegate type that gets coupled with the event.public delegate void OnStockLow(object sender, EventArgs e);

3) Next, the event StockLow is declared. Note how the Delegate is coupled with the Event. It implies that notification handler function should return void and receive the object as a first parameter and EventArgs as the second parameter. As it is a multicast delegate you can use delegate chain of above said functions. OK, Now the Product stock published the Event. Below is the declaration of the event:

//001_3: Published event (StockLow), that takes responsibility for sending notification to the subscriber through the above
//Specified multicast delegate
public event OnStockLow StockLow;

Note the syntax:
public event <delegate type name> <event_name>.

4) The constructor of the ProductStock class initialized the members 

ProductName and StockInHand. Below is the code:
//001_4: Constructor that Initializes the Stock
public ProductStock(string Name, int OpeningStock)
{
  ProductName = Name;
  StockInHand = OpeningStock;
}

5) The Counter objects call the ReduceStock function when a sale is performed. This function reduces the current stock and notifies the subscriber of the LowStock event when the current stock goes less than five. Below is the function implementation:

//001_5: This function reduces the stock based on the sales on the billing counters. When the stock in hand is lower than
//                                            5, it raises the event StockLow event.
public void ReduceStock(int SalesDone)
{
            StockInHand = StockInHand - SalesDone;
            if (StockInHand < 5 )
            {
                        EventArgs arg = new EventArgs();
                        StockLow(this, arg);
            }
}

Note that in the above function call to StockLow(this,arg) is known as raising the event or sending a notification.
We are done with the ProductStock class.

The Counter Class


1) The counter class declares the member variable for the counter name and the constructor initializes the Name. The Sales function takes the ProductStock and number of product sold. It makes a call to the ReduceStock after the sales. Below is the Implementation code:

//002: This class for Sales Counter that performs the Sales on different counters and makes the billing. This class
//                                Subscribe to the Published event and Receives notification through Multicast delegate.
public class Counter
{
            //002_1: Class member
            private string CounterName;

            //002_2: Constructor for Counter
            public Counter(string Name)
            {
                        CounterName = Name;
            }

            //002_2: Function that records the sales performed on the counter
            public void Sales(ProductStock prod, int howmuch)
            {
                        Console.WriteLine("{0} Sold {1} numbers", prod.ProductName, howmuch);
                        prod.ReduceStock(howmuch);
            }

2) The counter class implements the notification handler for StockLow. Note that the arguments and the void return type. Because the rule is expected by the delegate OnLowStock coupled with the event. Below is the handler:

//002_3: Function that acts as event handler for LowStock to receive the notification
public void LowStockHandler(object Sender, EventArgs e)
{
            Console.WriteLine("Anouncement on {0}: Stock of Product {1} gone Low",
                        CounterName , ((ProductStock) Sender).ProductName );
}

Client Code


Let us see how the client code works. Before that a small refresh on what we did. The ProductStock class exposes an event StockLow and that event is coupled to OnStockLow delegate. ReduceStock function raises the StockLow event when the product stock goes below five. The counter class implements the notification handler LowStockHandler to receive the notification. Where is the piece of code that links the LowStockHandler to the StockLow event? We will link that in the client code.

1) First the client creates the two billing counter objects. Below is the code for billing counter:

//Client 001: Create Billing Counters
Counter billing_counter1 = new Counter("Sindhu");
Counter billing_counter2 = new Counter("Ganga");

2) Next three ProductStock objects are created. These products will be sold through two counters. Below is code:

//Client 002: Create the Product Stocks
ProductStock prod1 = new ProductStock("Godrej Fridge", 7 );
ProductStock prod2 = new ProductStock("Sony CD Player", 6);
ProductStock prod3 = new ProductStock("Sony DVD", 800);

3) Next, we subscribe to the event LowStock published by the ProductStock class by creating the Delegate that points to the Notification handler function implemented in the Counter class. Below is the code:

//Client 003: Couple the Event with the Handler through the Delegate.
prod1.StockLow += new ProductStock.OnStockLow(billing_counter1.LowStockHandler);
prod2.StockLow += new ProductStock.OnStockLow(billing_counter1.LowStockHandler);
prod1.StockLow += new ProductStock.OnStockLow(billing_counter2.LowStockHandler);
prod2.StockLow += new ProductStock.OnStockLow(billing_counter2.LowStockHandler);  

4) Everything setup. So let as start selling the products and see the notification we stock goes below 5. I recommend you to put a breakpoint on the below piece of code and examine how the Events work. Below is the price code:

//Client 004: Now Let us Start serving the customers on the Queue on each counter
billing_counter1.Sales(prod1, 1);
billing_counter2.Sales(prod1, 2);
billing_counter2.Sales(prod3, 70);
billing_counter2.Sales(prod2, 1);
billing_counter1.Sales(prod2, 3);
billing_counter1.Sales(prod3, 5);

Output is shown below



Note: The Attached Solution is created in Dot Net 2003 for backward compatibility. If you have the latest version Say “Yes” to the conversion dialog.

Source Code: Download

No comments:

Post a Comment

Leave your comment(s) here.

Like this site? Tell it to your Firend :)