|
This tutorial shows how to declare, invoke, and hook up to events in C#. Further ReadingTutorialAn event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object. The most familiar use for events is in graphical user interfaces; typically, the classes that represent controls in the interface have events that are notified when the user does something to the control (for example, click a button). Events, however, need not be used only for graphical interfaces. Events provide a generally useful way for objects to signal state changes that may be useful to clients of that object. Events are an important building block for creating classes that can be reused in a large number of different programs. Events are declared using delegates. If you have not yet studied the Delegates Tutorial, you should do so before continuing. Recall that a delegate object encapsulates a method so that it can be called anonymously. An event is a way for a class to allow clients to give it delegates to methods that should be called when the event occurs. When the event occurs, the delegate(s) given to it by its clients are invoked. In addition to the examples on declaring, invoking, and hooking up to events, this tutorial also introduces the following topics:
Example 1The following simple example shows a class, For example, a word processor might maintain a list of the open documents. Whenever this list changes, many different objects in the word processor might need to be notified so that the user interface could be updated. By using events, the code that maintains the list of documents doesn't need to know who needs to be notified - once the list of documents is changed, the event is automatically invoked and every object that needs to be notified is correctly notified. By using events, the modularity of the program is increased. // events1.cs
using System;
namespace MyCollections
{
using System.Collections;
// A delegate type for hooking up change notifications.
public delegate void ChangedEventHandler(object sender, EventArgs e);
// A class that works just like ArrayList, but sends event
// notifications whenever the list changes.
public class ListWithChangedEvent: ArrayList
{
// An event that clients can use to be notified whenever the
// elements of the list change.
public event ChangedEventHandler Changed;
// Invoke the Changed event; called whenever list changes
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this, e);
}
// Override some of the methods that can change the list;
// invoke event after each
public override int Add(object value)
{
int i = base.Add(value);
OnChanged(EventArgs.Empty);
return i;
}
public override void Clear()
{
base.Clear();
OnChanged(EventArgs.Empty);
}
public override object this[int index]
{
set
{
base[index] = value;
OnChanged(EventArgs.Empty);
}
}
}
}
namespace TestEvents
{
using MyCollections;
class EventListener
{
private ListWithChangedEvent List;
public EventListener(ListWithChangedEvent list)
{
List = list;
// Add "ListChanged" to the Changed event on "List".
List.Changed += new ChangedEventHandler(ListChanged);
}
// This will be called whenever the list changes.
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
public void Detach()
{
// Detach the event and delete the list
List.Changed -= new ChangedEventHandler(ListChanged);
List = null;
}
}
class Test
{
// Test the ListWithChangedEvent class.
public static void Main()
{
// Create a new list.
ListWithChangedEvent list = new ListWithChangedEvent();
// Create a class that listens to the list's change event.
EventListener listener = new EventListener(list);
// Add and remove items from the list.
list.Add("item 1");
list.Clear();
listener.Detach();
}
}
}
OutputThis is called when the event fires. This is called when the event fires. Code Discussion
Events and InheritanceWhen creating a general component that can be derived from, what seems to be a problem sometimes arises with events. Since events can only be invoked from within the class that declared them, derived classes cannot directly invoke events declared within the base class. Although this is sometimes what is desired, often it is appropriate to give the derived class the freedom to invoke the event. This is typically done by creating a protected invoking method for the event. By calling this invoking method, derived classes can invoke the event. For even more flexibility, the invoking method is often declared as virtual, which allows the derived class to override it. This allows the derived class to intercept the events that the base class is invoking, possibly doing its own processing of them. In the preceding example, this has been done with the Events in InterfacesOne other difference between events and fields is that an event can be placed in an interface while a field cannot. When implementing the interface, the implementing class must supply a corresponding event in the class that implements the interface. .NET Framework GuidelinesAlthough the C# language allows events to use any delegate type, the .NET Framework has some stricter guidelines on the delegate types that should be used for events. If you intend for your component to be used with the .NET Framework, you probably will want to follow these guidelines. The .NET Framework guidelines indicate that the delegate type used for an event should take two parameters, an "object source" parameter indicating the source of the event, and an "e" parameter that encapsulates any additional information about the event. The type of the "e" parameter should derive from the EventArgs class. For events that do not use any additional information, the .NET Framework has already defined an appropriate delegate type: EventHandler. Example 2The following example is a modified version of Example 1 that follows the .NET Framework guidelines. The example uses the EventHandler delegate type. // events2.cs
using System;
namespace MyCollections
{
using System.Collections;
// A class that works just like ArrayList, but sends event
// notifications whenever the list changes:
public class ListWithChangedEvent: ArrayList
{
// An event that clients can use to be notified whenever the
// elements of the list change:
public event EventHandler Changed;
// Invoke the Changed event; called whenever list changes:
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this,e);
}
// Override some of the methods that can change the list;
// invoke event after each:
public override int Add(object value)
{
int i = base.Add(value);
OnChanged(EventArgs.Empty);
return i;
}
public override void Clear()
{
base.Clear();
OnChanged(EventArgs.Empty);
}
public override object this[int index]
{
set
{
base[index] = value;
OnChanged(EventArgs.Empty);
}
}
}
}
namespace TestEvents
{
using MyCollections;
class EventListener
{
private ListWithChangedEvent List;
public EventListener(ListWithChangedEvent list)
{
List = list;
// Add "ListChanged" to the Changed event on "List":
List.Changed += new EventHandler(ListChanged);
}
// This will be called whenever the list changes:
private void ListChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
public void Detach()
{
// Detach the event and delete the list:
List.Changed -= new EventHandler(ListChanged);
List = null;
}
}
class Test
{
// Test the ListWithChangedEvent class:
public static void Main()
{
// Create a new list:
ListWithChangedEvent list = new ListWithChangedEvent();
// Create a class that listens to the list's change event:
EventListener listener = new EventListener(list);
// Add and remove items from the list:
list.Add("item 1");
list.Clear();
listener.Detach();
}
}
}
OutputThis is called when the event fires. This is called when the event fires. See Also |
