Search code examples
c#eventsrouted-events

Why I can't pass RoutedEventHandler directly in C#


I got a UserControl where I want an event to be public:

public event RoutedEventHandler CloseButtonClicked;

But to dispatch to this event, the following doesn't work:

public MyUserControl()
{
    InitializeComponent();

    closeButton.Click += CloseButtonClicked;
}

While the following does work:

public MyUserControl()
{
    InitializeComponent();

    closeButton.Click += (sender, args) => CloseButtonClicked(sender, args);
}

Why is that?


Solution

  • The difference between your two scenarios is when the CloseButtonClicked event is evaluated.

    In the first, non-working example, the value of the event field is evaluated at the time that the program statement closeButton.Click += CloseButtonClicked; is executed. Unless you've already set the field to some useful value, then later when the Click event is raised nothing will happen.

    In the second, working example, the value of the event field is evaluated at the time that the Click event is raised. That statement declares an anonymous method (via the lambda expression) which on execution invokes the delegate instance stored in the CloseButtonClicked event field. So as long as that field gets set before the event is raised, it doesn't matter that the field wasn't set when you executed the closeButton.Click += (sender, args) => CloseButtonClicked(sender, args); statement.

    Note that even the second statement will fail, with a NullReferenceException, if the CloseButtonClicked event field hasn't been initialized when the Click event is raised. When raising events, you should protect against this by checking for null first (and preferably protecting against any thread race conditions by saving the value into a local first). For example:

    closeButton.Click += (sender, args)
    {
        RoutedEventHandler handler = CloseButtonClicked;
    
        if (handler != null)
        {
            handler(sender, args);
        }
    };
    

    Naturally, the above boilerplate is often encapsulated into a helper method (extension or otherwise).