Search code examples
c#eventsmessage

C# Bubbling/Passing Along An Event


How do I pass along an event between classes?

I know that sounds ridiculous (and it is) but I've been stumped on this for the past little while. Search didn't turn up a similar question so I figured I would pose it.

Here are the objects involved:

WinForm -> Speaker -> Tweeter
                   -> Woofer

[Speaker, Tweeter, Woofer] all declare a "SpeakToMe" event that sends a simple string message. The events are declared using the standard pattern:

public delegate void SpeakToMeHandler(object sender, SpeakToMeEventArgs e);
public event SpeakToMeHandler SpeakToMe;
protected virtual void OnSpeakToMe(string message)
{
   if (SpeakToMe != null) SpeakToMe(this, new SpeakToMeEventArgs(DateTime.Now.ToString() + " - " + message));
}

SpeakToMeEventArgs is a simple class inheriting from EventArgs & containing a string property (Message).

On their own, each of these events works fine. E.g., I set a button in the form to create, subscribe, and fire the event for [Speaker, Tweeter, Woofer]. Each reports back properly.

The problem is when Speaker creates a [Tweeter, Woofer] and subscribes to their events.

What I want is for [Tweeter, Woofer] to fire their event, Speaker to consume it and fire it's own event. I thought this should be very straight forward:

void tweeter_SpeakToMe(object sender, SpeakToMeEventArgs e)
{
   Console.Out.WriteLine("the tweeter is speaking: " + e.Message);
   this.OnSpeakToMe("tweeter rockin' out [" + e.Message + "]");
}

Stepping through this function (in Speaker), Console.Out.WriteLine works. Continuing to step through OnSpeakToMe, shows that the delegate is null.

Speaker's SpeakToMe event is subscribed to by the form. I understood that this should prevent the event's delegate from being null.

I'm sure this is an easy one, what am I missing?

Btw, in case you're curious as to why I'm looking for this. [Speaker, Tweeter, Woofer] are my demo stand-ins for a really long data processing operation. The form runs several of these concurrently and requires progress updates from each class.

As always, any and all help is greatly appreciated!

Update: Thanks for all of the feedback everyone. I really appreciate the help! I picked up a couple of good tips (@David Basarab & @Brian) and a few different ideas on how to structure things. Again, much appreciated!


Solution

  • If I understood what you wanted in the basic sense. Is to have the Tweeter and Woofer fire an event that the Speaker is subscribed too then fire its own.

    Here is my code that has this output

    OUTPUT

    OnSpeak Message = OnSpeakToMeHander Orginal Message: Fired By Tweeter

    OnSpeak Message = OnSpeakToMeHander Orginal Message: Fired By Woofer

    class Program
    {
    
        static void Main(string[] args)
        {
            Console.Clear();
    
            try
            {
                Speaker speaker = new Speaker();
                speaker.speakerEvent += new SpeakToMeHandler(Program.OnSpeak);
    
                // Cause events to be fied
                speaker.Tweeter.CauseEvent();
                speaker.Woofer.CauseEvent();
    
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: {0}", ex.Message);
                Console.WriteLine("Stacktrace: {0}", ex.StackTrace);
            }
        }
    
        public static void OnSpeak(object sendere, SpeakToMeEventArgs e)
        {
            Console.WriteLine("OnSpeak Message = {0}", e.Message);
        }
    
    }
    
    public delegate void SpeakToMeHandler(object sender, SpeakToMeEventArgs e);
    
    public class SpeakToMeEventArgs : EventArgs
    {
        public string Message { get; set; }
    }
    
    public class Speaker
    {
        public event SpeakToMeHandler speakerEvent;
    
        public Tweeter Tweeter { get; set; }
        public Woofer Woofer { get; set; }
    
        public void OnSpeakToMeHander(object sender, SpeakToMeEventArgs e)
        {
            if (this.speakerEvent != null)
            {
                SpeakToMeEventArgs args = new SpeakToMeEventArgs
                    {
                        Message = string.Format("OnSpeakToMeHander Orginal Message: {0}", e.Message)
                    };
    
                this.speakerEvent(this, args);
            }
        }
    
        public Speaker()
        {
            this.Tweeter = new Tweeter();
            this.Woofer = new Woofer();
    
            Tweeter.tweeterEvent += new SpeakToMeHandler(this.OnSpeakToMeHander);
            Woofer.wooferEvent += new SpeakToMeHandler(this.OnSpeakToMeHander);
        }
    }
    
    public class Tweeter
    {
        public event SpeakToMeHandler tweeterEvent;
    
        public void CauseEvent()
        {
            SpeakToMeEventArgs args = new SpeakToMeEventArgs()
                {
                    Message = "Fired By Tweeter"
                };
    
            if (this.tweeterEvent != null)
            {
                this.tweeterEvent(this, args);
            }
        }
    }
    
    public class Woofer
    {
        public event SpeakToMeHandler wooferEvent;
    
        public void CauseEvent()
        {
            SpeakToMeEventArgs args = new SpeakToMeEventArgs()
                {
                    Message = "Fired By Woofer"
                };
    
            if (this.wooferEvent != null)
            {
                this.wooferEvent(this, args);
            }
        }
    }