Saturday, October 23, 2004

Events/Delegates

Have you ever wondered whats the difference between events and delegates.
Yes/no whatever, just read on....
All the documentation that I have read makes them look like pretty differnt from each other. But, but just you wait.
My question is, what you can do with events, you can easily do with delegates? Right? Confused? Think...think :)


To get started check this example,

namespace DifferenceEvDel
{
using System;
delegate void BarkHandler(string s);


class DogApplication

{
public static event BarkHandler delegate1;
public static BarkHandler event1;
static void Main(string[] args)

{
delegate1 += new BarkHandler(DogBarking);
event1 += new BarkHandler(DogBarking);
delegate1("Bow!! Bow!! used delegate");
event1("Bow!! Bow!! used event");
}

static void DogBarking(string str)
{
Console.WriteLine(str.ToString());
}
}
}
Differnt approach, same result. Now you will agree on what I said in the begining.

MSDN clearly says :
"The event keyword lets you specify a delegate that will be called upon the occurrence of some "event" in your code."

Lets check out whats new in the events, point by point:

* Event can be invoked by the class which declared it. Delegate has no such restriction. Derived classes from the class declaring the event also aren't allowed to fire the event.

namespace DifferenceEvDel

{
using System;
delegate void BarkHandler(string s);

class BlueDog
{
public static event BarkHandler event1;
public static BarkHandler delegate1;
static void Main(string[] args)

{
delegate1 += new BarkHandler(DogBarking);
event1 += new BarkHandler(DogBarking);
new BlackDog().TeaseTheDog();
Console.ReadLine();
}

static void DogBarking(string str)
{
Console.WriteLine(str.ToString());
}
}

class BlackDog
{
public void TeaseTheDog()
{
BlueDog.event1("Bow!! Bow!! used events");
//The event 'DifferenceEvDel.BlueDog.event1' can only
//appear on the left hand side of += or -=
//(except when used from within the type 'DifferenceEvDel.BlueDog')
//Comment the above line to compile
BlueDog.delegate1("Bow!! Bow!! used delegate"); // compiles fine
}
}
}
An event of BlueDog can not be invoked from BlackDog, but a delegate can be.

*Accessor specifiers

When you put an access specifier(private, public etc.) on an event it controls who can register or listen to that event. Now, how would you specify the access control for the invocation of that event? In the question lies the answer :)

There will be two access modifiers like 'public public event' which is not so intutive. So as of now Event can be invoked by the class which declared it. But there are always ways to bypass this.

*Event can be included in an interface.

This one is the most important difference from the design perspective.
Try this in the same namespace
as above

namespace DifferenceEvDel
{
interface IDog
{
//Delegate in interface, error, comment this line to compile ;)
BarkHandler delegate1; //Err: Interfaces cannot contain fields
//Event in interface, its okey, Karry on I mean Carry on

event BarkHandler event1;
}

class Dog : IDog
{
public event BarkHandler event1; // Implement the interface
static void Main(string[] args) {}
}
}

*Events have an add and remove method(like get set).You can override these, so you can find out when somebody subscribes to your event.

public event BarkHandler event1
{
add
{
Console.WriteLine("Bow!! Bow!! I got added"); msgNotifier += value;
}
remove
{
Console.WriteLine("Bow!! Bow!! I got removed"); msgNotifier -= value;
}
}

My conclusion:
"Events are nothing more then a modifier on delegate type, Like all modifiers it adds some restrictions to be enforced by the compiler.

Keep watching this space for more events on delegates.

Friday, October 22, 2004

Holiday !!

This poem comes from a ten year old girl, whom I happen to know.
I rate it as one of the best poems I have ever read.
I feel swell whenever I read this :)

Holiday means a fun making day,
It comes on Sunday.
After all the hard work and play,
I rest for the whole day.
In the evening when the Sunday is coming to an end,
I look worried-
Mom says in an angry voice "Have you studied ?"
How can I say no ?
Because she will beat me so.
Now it is the time for Monday,
And again, after six days will come Sunday.
Because of MOnday all those who are sad,
They are all mad.
And don't realize that,
Sunday will come again, come again, come again.

Datasource for DataGrid

Our custom DataSource for Microsoft .Net DataGrid is finally working, fully!! I guess Chandra will not be able to find one more bug ;)
The problem was with the Allow New property.When you click in the last row of grid with asterisk, it changes to a pencil image.As soon as you click in the last row, the 'AllowNew' function of datasource (IBindingList.AddNew) is called.This adds a new object to the internal collection of data source. This object is in temporary state and its addition to datasoure will be cancelled if the user makes no changes to it and selects any other row or when he presses escape key. When you start typing in the last row a new row with asterisk sign will be added and the state of the object added to thecollection will be made permanent. The problem arises when the user does not makes any change and selects some already existing row.
In our datalist class as soon as the AddNew function is called we add an object and Raise an OnListChnaged event.But when the user cancels the operation the newly added row is removed from the Grids Internal Collection but there is no way we can get notified about this.The missing communication link can be taken care of by implementing IEditableObject in the class whose object will be stored in the DataList.But still there are problems. The DataGrid calls the item's IEditableObject.CancelEdit() function when you press ESC on an new row or activate a different row. But it never fires the ListChanged event nor does it remove the item from the data list. So there is no way for the list to remove the invalid new row from the data list.A new flag should be added, (called isNew) to the object that will be stored in the DataList.This should be exposed by either making it public or by using an internal property.By default it will be false but when a new object is added by using IbindingList.AddNew then it will be set to true using the exposed property.Check the implementation for more details and use of 'isNew'. As I don't feel like writing about it :)
For once the documentation/sample in MSDN are misleading and do not work.
Summary:
If you want to pass your own collection as DataSource for MS .NET DataGrid then implement IBindingList interface in your collection. If you want a strongly typed collection then use CollectionBase class as your base class otherwise use ArrayList. Most importantly, do not forget to implement IEditableObject interface in your classes whose objects you are going to store in your own collection.