Share via


How to: Create a Control that Responds to Events (C# Programming Guide) 

The following simple example shows a class, ListWithChangedEvent, which is similar to the standard ArrayList class but also invokes a Changed event whenever the contents of the list change. Such a general-purpose class could be used in numerous ways in a large program.

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.

When creating a general component that can be used as a base class for other components, you must account for the fact that events, unlike fields, 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 following example, this has been done with the OnChanged method. A derived class could call or override this method if necessary.

One 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.

Example

namespace TestCollections
{
    // A delegate type for hooking up change notifications.
    public delegate void ChangedEventHandler(object sender, System.EventArgs e);

    // A class that works just like ArrayList, but sends event
    // notifications whenever the list changes.
    public class ListWithChangedEvent : System.Collections.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(System.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(System.EventArgs.Empty);
            return i;
        }

        public override void Clear()
        {
            base.Clear();
            OnChanged(System.EventArgs.Empty);
        }

        public override object this[int index]
        {
            set
            {
                base[index] = value;
                OnChanged(System.EventArgs.Empty);
            }
        }
    }
}

namespace TestEvents
{
    using TestCollections;

    class EventListener
    {
        private ListWithChangedEvent m_list;

        public EventListener(ListWithChangedEvent list)
        {
            m_list = list;

            // Add "ListChanged" to the Changed event on m_list:
            m_list.Changed += new ChangedEventHandler(ListChanged);
        }

        // This will be called whenever the list changes.
        private void ListChanged(object sender, System.EventArgs e)
        {
            System.Console.WriteLine("This is called when the event fires.");
        }

        public void Detach()
        {
            // Detach the event and delete the list
            m_list.Changed -= new ChangedEventHandler(ListChanged);
            m_list = null;
        }
    }

    class Test
    {
        // Test the ListWithChangedEvent class.
        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();
        }
    }
}

Output

This is called when the event fires.
This is called when the event fires.

Robust Programming

  • Declaring an event

    To declare an event inside a class, first a delegate type for the event must be declared, if none is already declared.

    public delegate void ChangedEventHandler(object sender, System.EventArgs e);
    

    The delegate type defines the set of arguments that are passed to the method that handles the event. Multiple events can share the same delegate type, so this step is only necessary if no suitable delegate type has already been declared.

    Next, the event itself is declared.

    public event ChangedEventHandler Changed;
    

    An event is declared like a field of a delegate type, except that the keyword event precedes the event declaration, following the modifiers. Events usually are declared public, but any accessibility modifier is allowed.

  • Invoking an event

    Once a class has declared an event, it can treat that event just like a field of the indicated delegate type. The field will either be null, if no client has hooked up a delegate to the event, or else it refers to a delegate that should be called when the event is invoked. Thus, invoking an event is generally done by first checking for null and then calling the event.

    if (Changed != null)
    {
        Changed(this, e);
    }
    

    Invoking an event can only be done from within the class that declared the event.

  • Hooking up to an event

    From outside the class that declared it, an event looks like a field, but access to that field is very restricted. The only things that can be done are composing a new delegate onto that field, and removing a delegate from a (possibly composite) field.

    This is done with the += and -= operators. To begin receiving event invocations, client code first creates a delegate of the event type that refers to the method that should be invoked from the event. Then it composes that delegate onto any other delegates that the event might be connected to using +=.

    m_list.Changed += new ChangedEventHandler(ListChanged);
    

    When the client code is done receiving event invocations, it removes its delegate from the event by using operator -=.

    m_list.Changed -= new ChangedEventHandler(ListChanged);
    

See Also

Reference

Access Modifiers (C# Programming Guide)

Concepts

C# Programming Guide
Events (C# Programming Guide)
Delegates (C# Programming Guide)

Other Resources

Creating Event Handlers in Windows Forms