Every time the user clicks the Search button, first SearchButton_Click() is called, followed by NotifyStatistics(). The event handlers are called in the order they are added. Likewise, using -=, an event handler can be removed.
Now, you may be asking, why not just add whatever code is in the NotifyStatistics method to the SearchButton_Click method?
My Problem - User Control Interdependence
So, it should come as no surprise that I had a problem for which I found multicast delegates to be a good solution.
I was working on a site where I wanted an organization to be able to have an announcement on their page. I wanted this solution to be somewhat modular, so I wanted the UI code contained in an ASP.NET user control, that way I could use it for this purpose, as well as throw it on some other page if I had some other need for it in the future. I call this user control the announcement viewer control. Additionally, I needed the ability for a privileged user to be able to edit the announcement. And, I wanted this functionality to be in a separate ASP.NET user control. I wanted the user controls separate because the announcement editor user control may need to be on a different page than the announcement viewer control. So far, so good.
The problem occurred when I put the announcement viewer and announcement editor on the same page. When the privileged user edited the announcment and clicked the button to save, the page posted back to the server. On the server, both the announcement editor and annoucement viewer called their Page_Load methods, and the viewer loaded the current announcement from the database. Mind you, the user just changed the announcement and clicked the save button, but it hasn't gotten saved yet, because the method mapped to the save button hasn't been called yet. Then, the save method that is mapped to the save button is called, and the modified announcement is persisted to the database. However, the announcement viewer control has already loaded the announcement, so when the page renders in the browser, the announcement is correct in the announcement editor, but is the previous version in the announcement viewer control. To the user, this is unsettling. If the user refreshes the page, the correct version of the announcement will then get loaded into the announcement viewer control, and all is well. But, this is an unacceptable situation that will make the user question that the announcement change was really saved.
How do you solve this problem? I didn't want any direct connection between the announcement editor and announcement viewer because they wouldn't always be on the same page, and in some circumstances, may be used totally independently. What to do?
My Solution - Multicast Delegates
In my situation, the only entity that I wanted to have any knowledge (code) that these two controls had a relationship was the page that they were on. The page knows that it has both an announcement viewer and an announcement editor control. So, it was the page that I wanted handling the coordination between the two controls.
What I came up with was the idea that someone, my page in this case, could register an event handling method with the announcement editor control. Using the announcement editor's save button's multicast delegate for the click event, the registered event handling method would also get called when the announcement editor's save button was clicked, in addition to the editor's own method to actually persist the announcement to the database.
This allows the page to register a method with the announcement editor, and when the user clicks the save button, the page's method gets called. The only other issue, was adding a refresh method to the announcement viewer control, which was insignificant.
So, the flow is that the user clicks the editor's save button, the button's save method is called, persisting the announcement to the database. Next, the page's registered method is called, and that method calls the viewer's refresh method, which loads the just saved announcement back into the viewer.
This solution gives me the coordination between the two controls, without making either of them dependent on the other. Multicast delegates have saved my day!
The Code
In my announcement editor module, I have added the following public method:
public void RegisterForSaveNotification(System.EventHandler eventHandler)
{
this.SaveAnnouncementButton.Click += eventHandler;
}
In my announcement viewer module, I have added the following public method:
public void RefreshAnnouncement()
{
// My code to load the announcement from the database goes here.
announcementContent.InnerHtml =
Announcements.GetAnnouncement(EntityType.Text, EntityId.Text, Int32.Parse(Sequence.Text));
}
And, to tie it all together, here is the code I have added to the page that they are both on:
In Page_Load, I have added the following call to RegisterForSaveNotification():
RegisterForSaveNotification();
if (!Page.IsPostBack)
{
...
}
I show the call in context of the check for postback because the event handler must be registered for gets and posts. I would prefer to make the call in InitializeComponent(), but Visual Studio keeps deleting it when I put it there.
private void RegisterForSaveNotification()
{
// Register for save notification so we can have the announcement viewer update.
AnnouncementEditorControl1.RegisterForSaveNotification(new System.EventHandler(this.SaveAnnouncementNotification));
}
private void SaveAnnouncementNotification(object sender, System.EventArgs e)
{
// We have been notified that the announcement has been updated, so have the viewer refresh.
AnnouncementViewControl1.RefreshAnnouncement();
}
Summary