4

I understand that one can raise an event in the class that the implementation declaration occurs, but I wish to raise the event at the base class level and have the derived class's event be raised:

public interface IFoo
{
    event EventHandler<FooEventArgs> FooValueChanged;
    void RaiseFooValueChanged(IFooView sender, FooEventArgs  e);
}

[TypeDescriptionProvider(typeof(FooBaseImplementor))]
public abstract class FooBase : Control, IFoo
{
    public virtual event EventHandler<FooEventArgs> FooValueChanged;

    public void RaiseFooValueChanged(IFooView sender, FooEventArgs e)
    {
        FooValueChanged(sender, e);
    }
}

I cannot have the FooValueChanged event abstract, because then the base class cannot raise the event. Current the code runs, but the call FooValueChanged(sender, e) throws a NullReferenceException because it doesn't call the derived class's event, only that of the base class.

Where am I going wrong?

I can have the event and the raiser both abstract, but then I need to remember to call FooValueChanged(sender, e) in every single derived class. I'm trying to avoid this while being able to use the Visual Studio designer for derived controls.

Llyle
  • 5,980
  • 6
  • 39
  • 56
  • For anyone reading this question that's puzzled over why I have a TypeDescriptionProvider: http://www.urbanpotato.net/default.aspx/document/2001 – Llyle Jan 25 '09 at 01:04

3 Answers3

10

Note first, the event declaration that you are using is a short-hand notation in C#:

public event EventHandler Event;
public void RaiseEvent() {
    this.Event(this, new EventArgs());
}

Is equivalent to:

private EventHandler backEndStorage;
public event EventHandler Event {
    add { this.backEndStorage += value; }
    remove { this.backEndStorage -= value; }
}
public void RaiseEvent() {
    this.backEndStorage(this, new EventArgs());
}

Where backEndStorage is a multi-cast delegate.


Now you can rewrite your code:

public interface IFoo
{
    event EventHandler<FooEventArgs> FooValueChanged;
    void RaiseFooValueChanged(IFooView sender, FooEventArgs  e);
}

[TypeDescriptionProvider(typeof(FooBaseImplementor))]
public abstract class FooBase : Control, IFoo
{
    protected event EventHandler<FooEventArgs> backEndStorage;
    public event EventHandler<FooEventArgs> FooValueChanged {
        add { this.backEndStorage += value; }
        remove { this.backEndStorage -= value; }
    }

    public void RaiseFooValueChanged(IFooView sender, FooEventArgs e)
    {
        this.backEndStorage(sender, e);
    }
}

public class FooDerived : FooBase {
    public event EventHandler<FooEventArgs> AnotherFooValueChanged {
        add { this.backEndStorage += value; }
        remove { this.backEndStorage -= value; }
    }
}

So now when events are added on the derived class, they will actually be added to the backEndStorage of the base class, hence allowing the base class to call the delegates registered in the derived class.

earlNameless
  • 2,878
  • 20
  • 25
  • Good answer and nearly exactly what I was after. I'll post my final solution which uses your recommendation. Thanks. – Llyle Jan 25 '09 at 00:50
  • Note that based on [Hidden Features of C#](http://stackoverflow.com/questions/9033/hidden-features-of-c/9282#9282) you could do: `protected event EventHandler backEndStorage = delegate {};` and then not check for null. – earlNameless Mar 12 '11 at 16:24
2

The final result:

public interface IFoo
{
    event EventHandler<FooEventArgs> FooValueChanged;
    void RaiseFooValueChanged(IFooView sender, FooEventArgs e);
}

[TypeDescriptionProvider(typeof(FooBaseImplementor))]
public abstract class FooBase : Control, IFoo
{
    protected event EventHandler<FooEventArgs> backEndStorage;
    public abstract event EventHandler<FooEventArgs> FooValueChanged;

    public void RaiseFooValueChanged(IFooView sender, FooEventArgs e)
    {
        if (backEndStorage != null)
            backEndStorage(sender, e);
    }
}

public class FooDerived : FooBase {
    public override event EventHandler<FooEventArgs> FooValueChanged {
        add { backEndStorage += value; }
        remove { backEndStorage -= value; }
    }
}
Llyle
  • 5,980
  • 6
  • 39
  • 56
1

Why do you need to use an event? Can't you just use an overridden method. The base calls the overridable method, which is 'intercepted' by the derived class, which can then raise an event?

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541