Share via


How do I create an event for an Custom Control in C#

Question

Friday, July 17, 2009 11:02 PM

I normaly work with VB.NET, but now I am making an apps in C#.

I created a Custom Control and I need to create an event.

This is how I do it in VB.NET

'To create the event
Public Event ValueChanged(ByVal e As Integer)

'To Rais the event
RaiseEvent ValueChanged(24)

I need the C# equivalent of the code above.

All replies (6)

Wednesday, July 22, 2009 6:40 PM âś…Answered | 2 votes

This is one area where C# is *significantly* harder that VB.

First, in general in .NET the recommended practice is to create your events with two parameters: sender and eventArgs.

The code I then wrote to get the same affect as the VB code you have above is below.

First, you have to declare a delegate. I put mine just above the public partial class statement in my user control code:

    // Declare a delegate
    public delegate void ValueChangedEventHandler(object sender, ValueChangedEventArgs e);

Notice how I changed the signature from just e as Integer to sender and event args.

I then added the event to the control by putting this code *within* the user control class:

        // Declare an event
        [Category("Action")]
        [Description("Fires when the value is changed")]
        public event ValueChangedEventHandler ValueChanged;

I then added the method to process the event also in the user control class:

        protected virtual void OnValueChanged(ValueChangedEventArgs e)
        {
            // Raise the event
            if (ValueChanged != null)
                ValueChanged(this,e);
        }

Since there is no "RaiseEvent" in C#, you instead need to call the OnValueChanged method where you want to raise the event. In this example, I did it on the change of a textbox that resides in my user control.

        private void SampleUserControl_TextChanged(object sender, EventArgs e)
        {
            OnValueChanged( new ValueChangedEventArgs(24));
        }

Notice I hard-coded in the 24. You could put anything you wanted as the integer parameter instead.

Then, since your event does not use the standard EventArgs but rather uses a custom ValudChangedEventArgs, you need to create a class for that. I put this class in the same code file as the user control, but after the end of the user control class:

    public class ValueChangedEventArgs : EventArgs
    {
        public int NewValue { get; set; }

        public ValueChangedEventArgs(int newValue)
        {
            NewValue = newValue;
        }
    }

You can rename "NewValue" to something appropriate. I did not know what your "24" represented to put in something more specific.

YEA. Quite a bit more code.

This event can then be hooked up in the form containing this control as follows:

// Hook up the event
sampleUserControl1.ValueChanged += new ValueChangedEventHandler(EventTest);

This last statement can be simplified to this:

sampleUserControl1.ValueChanged += EventTest;

That's it.

If anyone knows of a way to simplify all of this ... I would love to hear about it as well.

Hope this helps.

www.insteptech.com ; msmvps.com/blogs/deborahk
We are volunteers and ask only that if we are able to help you, that you mark our reply as your answer. THANKS!


Saturday, July 18, 2009 1:08 AM

public event EventHandler<int> OnValueChanged;

private void SomethingHappened(object sender, EventArgs e)
{
    if(this.OnValueChanged!=null) this.OnValueChanged(this, new EventArgs<int>(24));
}

Maybe, not exactly the same, but the idea is there (I am not sure about EventArgs<int> - I created my own GenericEventArgs<T> class that I've been using)


Thursday, July 23, 2009 1:05 AM

Both of the above examples are nearly correct. Ruslans post assumes there is a generic EventArgs<T> class that I can't find in C#, and he used EventHandler<int> instead of EventHandler<EventArgs<int>> in his event declaration.

Deborah's is more explicit, explains what's going on and will actually work... but she missed the key trick from Ruslan's post, which is using generics to simplify the process (and this is what's recommended by FxCop and VS code analysis).

You don't need to create a delegate for the event. Instead of;

 // Declare a delegate
    public delegate void ValueChangedEventHandler(object sender, ValueChangedEventArgs e);

Do nothing, then when you declare the event use generics;

        // Declare an event
        [Category("Action")]
        [Description("Fires when the value is changed")]
        public event EventHandler<ValueChangedEventArgs> ValueChanged;

Then OnValueChanged becomes;

        protected virtual void OnValueChanged(int newValue)
        {
            // Raise the event
            if (ValueChanged != null)
                ValueChanged(this, new ValueChangedEventArgs(newValue));
        }

Connecting to the event becomes;

sampleUserControl1.ValueChanged += new EventHandler<ValueChangedEventArgs>(EventTest);

but if you hit tab after typing the += the IDE will fillout the rest of the line for you.

You still need to create an event arguments class.

The benefit to this pattern over the VB one is if you need to add more arguments later you can do so without breaking older event handlers in other applications... you just create a ValueChangedEventArgs2 (probably with a better name) that inherits from ValueChangedEventArgs. The old event handler will then still work... and even if you don't do the whole inheritence thing it still leads to less refactoring in your code when you add or remove event args as you're not changing the signature of the event handlers.

It IS annoying that you have to create event arg objects, but it's generally worth it on big projects in the long run. You could create your own generic event args object to simplify the process for events with one argument, something like this;

public class EventArgs<T> : EventArgs  
{

  private T m_Value;

  public EventArgs(T value) 
  {
    m_Value = value;
  }

  public T Value
  {
     get { return m_Value; }
     set { m_Value = value; }
  }
}

The only problem with this is that it makes using the events a bit ugly and breaks FxCop rules because you end up nesting generic types, i.e

        // Declare an event
        [Category("Action")]
        [Description("Fires when the value is changed")]
        public event EventHandler<EventArgs<int>> ValueChanged;

Your choice as to whether you do this or not.


Thursday, July 23, 2009 1:25 AM | 1 vote

From "Practical Guidelines and Best Practices for Microsoft Visual Basic and Visual C# Developers"

P.189 17.7 Delegate invocation

"Always assign a delegate object to a temporary variable before invoking the delegate, and check that the temporay variable isn't null before invoking the delegate..."

This:
 protected virtual void OnValueChanged(ValueChangedEventArgs e)
        {
            // Raise the event
            if (ValueChanged != null)
                ValueChanged(this,e);
        }

Should be:
 protected virtual void OnValueChanged(ValueChangedEventArgs e)
        {
            // Raise the event
            EventHandler<ValueChangedEventArgs> handler = this.ValueChanged;
            if (handler != null)
                handler (this, e);
        }


John Grove - TFD Group, Senior Software Engineer, EI Division, http://www.tfdg.com


Thursday, July 23, 2009 1:45 AM

Also note, it's generally considered good practice to declare variables like ints and bools as 'ReadOnly' and not provide a setter on their property in the EventArgs class unless you except event handlers to change them (like in the case of a cancel property).

This would look like this;

public class IntValueChangedEventArgs : EventArgs  
{

  private readonly int m_Value;

  public EventArgs(int value) 
  {
    m_Value = value;
  }

  public int Value
  {
     get { return m_Value; }
  }
}

That will prevent an event handler changing the value and messing up the processing of any event handlers that follow it.


Thursday, July 23, 2009 1:55 AM

Hi John,

Thanks for pointing that out, I forgot about it in my post.

To explain to the person asking the question;
This is required in the case of mulit-threaded apps to prevent a race condition where the thread could switch between the if (handler != null) and handler(this, e) lines and that could then result in a null reference exception despite the if condition.