Events and Delegates Under The Hood – Reposted

MiniTutorial

Many programmers come to Silverlight with little prior experience with C#, and thus conceptualize events as a response to an action that is “hooked up” using somewhat arbitrary syntax.  All of that is fine, until it isn’t, and so this post will dive a bit deeper into Delegates and Events as core aspects of .Net languages.

Delegates

Birthday 2010 When a head of state dies, the president of the United States typically doesn’t have time to attend the funeral personally. Instead, he dispatches a delegate. Often this delegate is the vice president, but sometimes the VP is unavailable and the president must send someone else, such as the secretary of state or even the “first lady.” He doesn’t want to “hardwire” his delegated authority to a single person; he might delegate this responsibility to anyone who is able to execute the correct funeral-protocol. The president defines in advance what responsibility will be delegated (attend the funeral), what parameters will be passed (condolences, kind words, warnings to potential usurpers), and what value he hopes to get back (good will, oil). He then assigns a particular person to that delegated responsibility at “runtime” as the course of his presidency progresses.

Some of the material in this article was stolen borrowed adapted from Programming C# 5th Edition.

Events

In programming, you are often faced with situations where you need to execute a particular action, but you don’t know in advance which method, or even which object, you’ll want to call upon to execute it. The classic example of this is the method called to handle a button press, a menu selection or some other “event.”

I Made The Button, You Handle What Happens When It Is Clicked

The programmer who creates a control such as a button often will not be the programmer who puts that button into an application.  The control writer defines what events the control will support (click, selection changed, etc.) and the control user defines what happens for the events the control user is interested in responding to. Note that the control user need not react to every event, but on the other hand, adding a button that doesn’t react to being pressed is frowned upon. The way the control user indicates what is to happen is to write a method that will handle the event, and then tell the event which method to call when that event is “fired.”  In C++ you could pass the address of a method as a parameter to other methods. In C# you can create an object that holds the address of the method (and that holds other useful information as well.) Such an object is called a delegate. There are many uses for delegates but the big two are event handling and call backs (“do some work, and when you are done, call this method”).

Creating A Custom Control and A Custom Event

Technically, a delegate is a reference type used to encapsulate a method with a specific signature and return type (and even more technically, the return type is not part of the signature of a method).  You can encapsulate any matching method in that delegate. To see how this works, let’s create a very simple custom control (in fact, I’m not going to give it any UI!).  My Timer control will check the time using a Dispatch Timer and will provide a SecondChanged delegate that other controls (for example, the MainPage) can register a method with.

Creating Delegates

You create a delegate with the delegate keyword, followed by a return type and the signature of the methods that can be delegated to it, as in the following:

public delegate void SecondChangedHandler( 
             object sender, TimeInfoEventArgs e );

This declaration defines a delegate named SecondChangedHandler which will encapsulate any method that takes as its first parameter an object of type Object (the base class for life, the universe and everything), and as its second parameter an object of type EventArgs (or anything derived from EventArgs) and that returns void.

While delegates can encapsulate any signature and return value, all event handlers in .NET return void and take an object as their first argument, and an EventArgs (or a class derived from EventArgs) as their second. Since the first parameter is a reference to the class that raised the event, by convention it is called sender, and by convention the second parameter is named e

For example, the SecondChangedHandler delegate defined above could encapsulate (among others) the following two methods

public void onSecondChanged(object sender, EventArgs e)
{
    MessageBox.Show(“Your time is up!”);
} 

public void myFunkyEventHandler(
   object sender,
   EventArgs e)
{
    myTextBlock.Text = “Tempus Fugit!”;
}

Assigning A Method To A Delegate

You assign which method will be called through the delegate using the assignment operator or the += operator, and you remove methods using the -= operator. For example, you might write:

// define the delegate
public delegate void SecondChangedHandler( object sender, TimeInfoEventArgs e );

// The public member variable SecondChanged is an instance of the 
//  SecondChangedHandler delegate
public SecondChangedHandler SecondChanged;

// assign method onSecondChanged
SecondChanged  = new  SecondChangedHandler(myHandlerMethod);

This syntax removes any previously assigned methods from the delegate and assigns the one you’ve provided.  To avoid inadvertantly removing a method that was previously assigned, you can, instead, use the plus-equals operator to add a method to the delegate:

SecondChanged  += new  SecondChangedHandler(myHandlerMethod);

The Event Operator’s Raison D’être Because inadvertently unregistering a previously registered handler is considered a bad thing to do, the event keyword was added to restrict how delegates are used: specifically to restrict their usage to the way they should be used when handling events: In short, events are delegates that

  • Can only be registered with += and not with the assignment operator (=)
  • Can only be called by methods of the class that defines the event

Let’s walk through a complete example.

A Custom Control and its Event

Create a new application, Clock and immediately add a new class, Timer.   Timer will have three public properties and a nested class for its specialized EventArgs…

namespace Clock
{
    public class Timer
    {
        public delegate void SecondChangedHandler(
           object sender, TimeInfoEventArgs e );
        public event SecondChangedHandler SecondChanged;

The Timer class will check the time periodically and then raise its SecondChanged event if the time has changed.

The constructor kicks off the Run method which uses a DispatcherTimer.  The interval is set to one second, and each time a Tick event is raised  work is done through the anonymous delegate (see the highlighted lines)

private void Run()
{
    var timer = new DispatcherTimer();

    timer.Tick +=
        delegate
        {
            var now = DateTime.Now;
            if ( now.Second != second )
            {
                var e = new TimeInfoEventArgs(
                      now.Hour, 
                      now.Minute, 
                      now.Second );
                OnSecondChanged( e );
                hour = now.Hour;
                minute = now.Minute;
                second = now.Second;
            }
        };

    timer.Interval = new TimeSpan( 0, 0, 1 ); // one second
    timer.Start();
}

For this to work, we’ll need to define TimeInfoEventArgs and the method OnSecondChanged, as well as the member variables hour, minute, second. Here is the complete Clock.cs file:

using System;
using System.Windows.Threading;

namespace Clock
{
    public class Timer
    {
        public delegate void SecondChangedHandler( 
                  object sender, TimeInfoEventArgs e );
        public event SecondChangedHandler SecondChanged;

        private int hour;
        private int minute;
        private int second;

        public Timer()
        {
            this.Run();
        }

        protected virtual void OnSecondChanged( 
                   TimeInfoEventArgs e )
        {
            if ( SecondChanged != null )
            {
                SecondChanged( this, e );
            }
        }

        private void Run()
        {
            var timer = new DispatcherTimer();

            timer.Tick +=
                delegate
                {
                    var now = DateTime.Now;
                    if ( now.Second != second )
                    {
                        var e = new TimeInfoEventArgs(
                            now.Hour,
                            now.Minute,
                            now.Second );
                        OnSecondChanged( e );
                        hour = now.Hour;
                        minute = now.Minute;
                        second = now.Second;
                    }
                };

            timer.Interval = new TimeSpan( 0, 0, 1 );
            timer.Start();
        }

        public class TimeInfoEventArgs
        {
            public TimeInfoEventArgs(
                int hour,
                int minute,
                int second )
            {
                Hour = hour;
                Minute = minute;
                Second = second;
            }

            public int Hour { get; private set; }
            public int Minute { get; private set; }
            public int Second { get; private set; }
        }
    }
}

To keep the example simple, I’ll just use a TextBlock in MainPage to display the current time. Here’s the Xaml:

<UserControl x:Class="Clock.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <StackPanel x:Name="LayoutRoot" Background="White" Orientation="Horizontal">
      <TextBlock Text="Current time: " />
      <TextBlock x:Name="CurrentTime" Text="12:00:00" />
   </StackPanel>
</UserControl>

In the code behind file (or the ViewModel if you are using MVVM) we’ll add the logic to register the Timer control and to respond to the second changing:

using System.Globalization;
using System.Windows;
using System.Windows.Controls;

namespace Clock
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            Loaded += new RoutedEventHandler(MainPageLoaded);
        }

        void MainPageLoaded(object sender, RoutedEventArgs e)
        {
            var t = new Timer();
            t.SecondChanged += UpdateDisplay;
        }

        void UpdateDisplay(object sender, Timer.TimeInfoEventArgs e)
        {
            CurrentTime.Text = e.Hour.ToString("D2")
                + ":" + e.Minute.ToString("D2")
                + ":" + e.Second.ToString("D2");
        }
    }
}

In the loaded event we instantiate our custom control and register that when the SecondChanged event is raised the method UpdateDisplay is to be called.

That method retrieves the Hour, Minute and Second from our custom TimeInfoEventArgs parameter.

My sincere apologies for the confusion in the original posting.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Languages, Mini-Tutorial, Patterns & Skills, Tools and Utilities and tagged , . Bookmark the permalink.

8 Responses to Events and Delegates Under The Hood – Reposted

Comments are closed.