I would not say that this is a design error, there are good reasons why the main form wants to listen to what the object does. One of the scenarios I encountered displays status messages for the user to indicate which background processes are running, or what several controls are doing in a multi-threaded application that allows you to open multiple screens / "pages" at the same time.
In the Application Composite UI block, the basic equivalent of the dependency injection container provokes events when its copying objects in the same work item (the work item is only an object container for a group of related user controls). He does this by looking at special attributes, such as [EventPublication("StatusChanged")] for events and [EventSubscription("StatusChanged")] for public methods. One of my applications uses this functionality so that a user control embedded in internal applications can transmit status information (for example, “Downloading client data ... 45%”) without knowing that this data will be the status bar of the main form.
So UserControl can do something like this:
public void DoSomethingInTheBackground() { using (StatusNotification sn = new StatusNotification(this.WorkItem)) { sn.Message("Loading customer data...", 33);
... where the StatusNotification class has an event with a signature of type
[EventPublication("StatusChanged")] public event EventHandler<StatusEventArgs> StatusChanged;
... and above, the Message() and Dispose() methods in this class raise this event accordingly. But this class clearly did not associate this event with anything. An object instance will automatically attach events to anyone who has a subscription attribute with the same name.
So, MainForm has an event handler that looks something like this:
[EventSubscription("StatusChanged", ThreadOption=ThreadOption.UserInterface)] public void OnStatusChanged(object sender, StatusEventArgs e) { this.statusLabel.Text = e.Text; if (e.ProgressPercentage != -1) { this.progressBar.Visible = true; this.progressBar.Value = e.ProgressPercentage; } }
... or some. This is more complicated since it will rotate through several status notifications in a certain number of seconds, since several user controls can broadcast status messages at the same time.
So, to recreate this behavior without switching to CAB (which, frankly, is much more complicated than I think it really should be), you can either have a MessageNotificationService object that you pass around your application or that you turn into a static / a single object (usually I avoid this approach, as it is harder to check), or you could create sub-user controls using the factory class, which will post events for you. Objects can be registered using the factory with attributes of your own creation or by explicitly calling methods that say "hey, when you create an object with the event of this signature, I want to know about it."
Just be careful that any class that you execute dispatches events when the object is deleted, because in this scenario it is stupid to end up with something that won't receive garbage collection.
Hope this helps!