Subscribe to Martyr2's Programming Underground        RSS Feed
***** 1 Votes

Passing Data Between Forms - Using Delegates And Events

Icon 2 Comments
Back in an article I wrote in 2010 titled "Passing Data Between Forms in C# or VB.NET", I described one of a couple methods for passing data between two forms in a .NET forms project. One method was to publicly expose a method on a child form that the parent form could then call on when needed. At the same time I also mentioned, and advised against, using a public property. As with many things in programming, there is always more than one way to skin a cat. Ok, in programming there may be 10 ways to skin a cat, 5 of them inadvisable, 2 of them buggy, 1 so cryptic that it is impossible to use and that leaves us with only a couple "real" options. In this article I will discuss another way to pass data between forms... using delegates and events to notify other forms that something has happened.

What is a delegate?

I always found delegates to be a bit of a cryptic topic in that everywhere you go you find really vague or round-a-bout explanations of what they were. It wasn't until I ran into an article in C++ about function pointers that it really clicked what delegates in .NET were, they are essentially pointers to functions! Only after awhile did I see that explanation pop up on MSDN for delegates. Way to go Microsoft on finally getting around to simplifying the explanation!

So to help you guys get the gist of it, just remind yourself that a delegate simply points at zero or more functions that match a specific signature. Thus when we call on a delegate, we are essentially calling zero or more functions at the same time.

If you think of this much like a radio or TV broadcaster, a delegate is like a station or channel. People can "subscribe" to that station or channel and anything broadcast out on that station or channel can be picked up by anyone listening to it.

What is an event?

Well events are a little simpler to understand. They are basically messages sent out to notify them that an action of some sort has happened. In our analogy of the radio or TV broadcaster, an event would be a signal to tell you that a show is starting or a signal to tell your PVR that it is time to start recording. Your PVR would listen for a "show is about to begin" event and choose whether or not to do anything in response to it.

Put this together with a delegate and an event notifies subscribers that something happened by calling on the delegate to tell them about it. In our project one form can setup a station or channel (a delegate), attach it to an event and let other forms subscribe or "listen" to that event. That way when we click a button or enter some text in a form control, we can notify other forms that the button was clicked and they can choose whether or not to respond.

Our Project Example

Ok so to demonstrate how ridiculously simple this is, we are going to setup a project which has two form classes in it. One is going to be our "parent" form that will spawn child forms, attach the child form's method to an event (make them a subscriber) and notify them when we do something. This parent form will have two buttons. One to create a child form, the other to send an event which will be picked up by the children. Child forms will simply have a textbox to display the message when they hear the event. Each child could do whatever they want with the event from altering the message to not even choosing to do anything at all. Just because you listen doesn't mean you need to act!

Let's start with the code for our parent form...

public partial class Form1 : Form {
        public Form1() {
            InitializeComponent();
        }

        // Our delegate (which "points" at any method which takes an object and EventArgs)
        // Look familiar? This is the signature of most control events on a form
        public delegate void SendMessage(object obj,  EventArgs e);

        // Here is the event we trigger to send messages out to listeners
        public event SendMessage OnSendMessage;

        // When we click the message button, tell others to respond to our button being clicked
        private void SendMessageButton_Click(object sender, EventArgs e) {
            if (OnSendMessage != null) {
                OnSendMessage(this, e);
            }
        }

        // This event creates our second form, says "attach its MessageReceived function to the event"
        private void CreateChild_Click(object sender, EventArgs e) {
            Form2 child = new Form2();
            OnSendMessage += child.MessageReceived;
            child.Show();
        }
}



The four key pieces of information here are the creation of a delegate that defines the type of functions it can point at, the event which is of the delegate's type, the calling of the event (giving it any parameters if required) and lastly attaching methods to the event so when the event is triggered it will call those functions.

In the code above you see our delegate points at functions which will return void and takes an object and EventArgs parameter. While this signature could be anything, our signature matches most control event methods (like button clicks etc). Next, we create our child forms one by one. See how we add "child.MessageReceived" to the event on line 23? This is saying that when the event is triggered by the button being clicked, call that function on the child form.

Let's now take a look at a child form to see what happens there...

public partial class Form2 : Form {
        public Form2() {
            InitializeComponent();
        }

        public void MessageReceived(object sender, EventArgs e) {
            textBox1.Text = "Message received from form 1";
        }
}



Not a whole lot of action here as you can see. But when the child form receives a notice that the button on the parent was clicked, it will activate that MessageReceived event, receive two parameters (sender being the parent form... and yes that means you could ideally reach through that and access the parent form's public data members) and set the textbox on the child form to our specific message. Pretty simple huh? Like I said when we call the event it will call all subscribers, one right after the other. This means you could spawn 20 child forms and they would all be subscribed to the same event.

You might be asking if you can add additional (perhaps custom) parameters to the event, add the additional parameter to the child's MessageReceived and thus passing custom data through the parameter. The answer would be a resounding YES! This means you could pass a complex object, maybe with the state of every item on your parent form, to the child and have the child do whatever it wants with the data. Maybe the child needs to see the value of a progress bar on the parent. You could pass that value into a custom object, give it to child elements and if the child forms care, they will look at the progress value and activate controls on themselves.

This way of communication is "pushing" data out to child elements, but can you push from children back? Sure. Create a delegate and event on your children, use the "sender" parameter to subscribe the parent form to the event and trigger your own events to notify the parent.

I kept the example above as simple and bare bones as possible to show you all the required pieces and how they fundamentally fit together. The only thing left to explain is why we test the event for null before calling it. Well, remember when I was talking about "zero" or more listeners? Well there is the possibility that no one had subscribed to the event and in that case the event would be null meaning it isn't pointing at any methods. So be sure to always check for this before triggering an event.

Conclusion

In the proceeding article we talked about a method to pass data from one form to another using delegates/events to "broadcast" actions to all the child forms who "subscribed" to the event. While this method is good for letting parent forms inform their children, we briefly talked about the idea of allowing the children to push notices back up to parent using the same technique. This process is good for a one-to-many situation and may be a little less useful in direct one-to-one form communication. I hope between this method and the ways I talked about in my previous article, you have all the answers you could ever want to describe how to get two forms to talk to one another. Thanks again for reading! :)

If you are looking for more fun little projects like this and want to expand your mind, your skills in .NET, or any other programming language, I encourage you to check out our ebook titled "The Programmers Idea Book 200 Software Project Ideas And Tips To Developing Them" which is available on The Coders Lexicon.

2 Comments On This Entry

Page 1 of 1

andrewsw 

31 October 2014 - 02:14 PM
Thank you, excellent. I think I've got it!

What has confused me before is the use of the term listening (subscribing is more accurate), together with the suggestion that the parent-form is oblivious - and doesn't care - who is listening.

This made me think that the parent just broadcasts an event; that is, broadcasts it widely, to any control/object that cares to listen. When we consider this possibility though, it isn't feasible. At any one time there might be 50 controls, or even 100 objects (of some kind) that might be capable of responding to an event. All of these objects would have to be notified, of every event!

So my interpretation (please correct me if I'm wrong) is that, yes, there are subscribers, but the parent (the broadcaster) has to register these subscribers. It needs to be aware of them, and maintain a list of them, so that it can notify them of the event. The parent doesn't care what the subscribers do in response to the event, but it still knows of their existence.

It is an interesting, and very important, topic, but not the easiest to get your head around!
0

Martyr2 

02 November 2014 - 11:47 AM

andrewsw, on 31 October 2014 - 02:14 PM, said:

...but the parent (the broadcaster) has to register these subscribers. It needs to be aware of them, and maintain a list of them, so that it can notify them of the event. The parent doesn't care what the subscribers do in response to the event, but it still knows of their existence.


Yeah you are 98% of the way there. The parent has to only be aware of them to the point of adding them to the event but after that, the parent really doesn't care. All it does is activates the event and anyone listening is notified. Think of it like a mailing list. Yeah we care to get the person signed up, but after that we rarely (if ever) manage users from that point. We blast out emails to a list and whoever is there to listen gets the email. They may decide to junk it, they might choose to forward it on, they may choose to read it and implement its contents in their business or whatever. As far as we know, we blasted it out to a list.

But yeah you are pretty much on the money. Events are just a list of function pointers to the subscribers. When you call the event you are actually triggering a function call on a list, going through each one one by one. Does this mean triggering an event can be slowed down at some point? Most certainly! If we had thousands of controls listening, calling an event could indeed take time as it processes each call. Which is part of the reason you would never want to have thousands of controls subscribed to an event and have that event fire several times a second (like a mouse move). You would see the app grind to a halt.

But thanks for reading. Sounds like you nailed the idea. :)
1
Page 1 of 1

July 2018

S M T W T F S
1234567
891011121314
1516171819 20 21
22232425262728
293031    

Recent Entries

Recent Comments

Search My Blog

1 user(s) viewing

1 Guests
0 member(s)
0 anonymous member(s)