I will finish “Build your own CAB” at least before Acropolis hits and makes it all obsolete. In the meantime, check out all the stuff that’s gone before:
- The Humble Dialog Box
- Supervising Controller
- Passive View
- Presentation Model
- View to Presenter Communication
- Answering some questions
- What’s the Model?
- Assigning Responsibilities in a Model View Presenter Architecture
- Domain Centric Validation with the Notification Pattern
- Unit Testing the UI with NUnitForms
- Event Aggregator – This post
- Stopping Event Propagation with the “Latch” – Very closely related follow up to this post. Forthcoming
- Embedded Controllers – Forthcoming
- MicroControllers – Forthcoming
- Other stuff…
And no, there will definitely NOT be a “Build your own Acropolis” series here.
Event Management is Hard
A direct quote from my tester today: “user interfaces are complicated critters.” From time to time I bump into the idea that Graphical User Interface (GUI) programming is easy stuff suitable for keeping the amateurs busy while the adults are busy on the server side. My previous job involved very complex business logic behind a trivial GUI, but in systems like my current project the complexity of the GUI dwarfs the web services and database. There are several things that potentially make GUI programming hard, but the worst culprit by far to me is the event driven nature of heavy, interactive clients. A close second might be the synchronization of state between different parts of the user interface. Here’s an example to illustrate what I mean:
- A grid control. Given some data, an array of Column objects and ColorCoding objects, and a page size, make an HTML table on the fly. Allow sorting from the grid
- A pager control
- A filter control that allowed you to filter data in the grid based on the unique values of the data set. Think lot’s of dropdowns
- A control that allowed you to customize the columns and color coding conditions
- A control that displayed the report header information
Fine and great, except I ran into two major problems. The first problem was that I needed to synchronize all of the various components of the logical “Report” anytime things changed (new data, filter changed, sorting, turning columns on and off, changing to a different report, etc.). What Martin Fowler calls flow synchronization turned out to be very clumsy. Besides the “n squared” communication problem, not every page had the exact same set of components. Having each component try to reach out and tell each other anytime the report state changed turned out to be a nightmare. Plus, what if I wanted to add completely new types of components to new report screens?
My second problem was the cascading event problem that’s so painfully common in stateful GUI programming. Resetting the data in the grid causes the filter control to refresh it’s dropdown boxes, which could easily fire off the onchanged event of the dropdowns, which would fire off a command to reset the grid data, which could… …ripple all over the place.
I came to the conclusion that I needed to funnel all the events to a single class that would be responsible for propagating the events to all the other class’s, and also to control the event propagation somewhat to shut down the rippling event problem. New components could be added to the screen and take part in the reporting workflow just by being registered as listeners to my new “ReportManager” class. What I came up with in the end was an implementation of the Event Aggregator design pattern.
The Event Aggregator pattern is essentially a Publish/Subscribe infrastructure inside your WinForms application. The Event Aggregator serves the same basic purpose as a messaging broker in a hub and spoke messaging architecture. If you have any experience (that isn’t repressed memory) with publish/subscribe messaging architectures like webMethods or Tibco this should be old news. We can take much of the writings and patterns of loosely connected messaging and apply those lessons to the construction of composite user interfaces. To handle all these events flying around we can compose our system into three groups:
- Subscribers that need to be told about events
- Publishers that raise the events in the first place
- A “Hub” that collects all of the events from the publishers and routes and/or transforms the events to the proper subscribers
By channeling all of the events into a single hub we make it much easier to add new components to our user interface. This is absolutely crucial for the kinds of composite applications we’re trying to build now with WinForms solutions.
Example Event Aggregator in StoryTeller
Here’s the setup. StoryTeller is, or will be shortly, a tool for editing and running acceptance tests with the FitnesseDotNet engine. The WinForms client is meant to replace the FitNesse Wiki for running tests, and as you can easily imagine, there are a lot of screen updates when test changes or is executed. Potentially, each test execution could effect:
- The TreeView control in the left pane that presents the Test/Test Suite hierarchy. The TreeView nodes that represent tests change their icon to reflect the test’s status as unknown, successful, failed, or exception. Suspiciously similar to the NUnit GUI and every graphical test runner you’ve ever seen in your life.
- If it’s open, the screen for editing a test. Some options are only enabled when there is a previous result. Visual cues need to change based on the result or state of the test. When the test is changed I gray out the title bar. When a test fails you get lots of red in the screen to indicate failure.
- Test grid pages. I wanted the grid to update (turn the grid row red or green, update the counts, etc.) when the Tests change state.
All three of these UI elements need to be updated on the same events, but it would be foolish to tie all of these pieces together with the nonvisual code that actually executes the tests. In Fowler speak, Flow Synchronization would lead to spaghetti code and undesirable coupling. Instead, I used a centralized Event Aggregator class to perform Observer Synchronization.
Most of the literature on using or building Event Aggregator’s in WinForms applications revolves around using “Attribute magic” to define event subscribers and publishers. I understand a little now about why people were so upset or dubious with my “roll your own CAB” statements. The generic way CAB does Event Aggregator *is* complicated (but well within the reach of mere mortals if you were so inclined to do it). When I say that you don’t need the CAB it’s not because I think I can quickly roll my own CAB, it’s because I think I can write little specific point solutions and quite possibly be better off for it.
My advice is to avoid the generic one size fits all Event Aggregator implementation for something specific to your own application with interfaces that are expressed in the terms of your problem domain. Make your Event Aggregator publish and subscribe events that are specific and meaningful to your problem domain. By making the events specific, the semantics of the code should be far more intention revealing than code like EventBroker.RaiseEvent(“event://some string/more string/event name”, new GenericEventArgs()) can ever be. I’m plenty happy to write a little bit of custom, specific code if the payoff is better understandability.
For StoryTeller, I took a very simple, direct path to creating an event aggregator for testing events. Let’s ignore the publisher and hub pieces of event aggregation for now. The subscribers in StoryTeller need to expose and implement an Observer interface called ITestObserver with four methods:
public interface ITestObserver
void StateIsUnknown(Guid testId);
void StartedRunning(Guid testId);
void Completed(TestResult result);
void Queued(Guid testId);
What I was aiming for with this design was semantic clarity and a strong contract. From an implementation standpoint it becomes pretty easy to organize the code that responds to test events. The first class to get the ITestObserver treatment was the TreeNode subclass for StoryTeller Test’s:
public class TestNode : LeafNode<Test>, ITestObserver
TestNode is a descendent of TreeNode that represents a Test within the TreeView explorer view. TestNode changes it’s display when any of the ITestObserver methods are called. For example, when a test run starts the StartedRunning() method is called. The TestNode will change its icon to a question mark to denote that the outcome is unknown, and change the TreeNode.Text to “name of the test (Executing)” to give some visual indication of which test is currently being executed. Here are a couple of the observer methods are just this:
public void StateIsUnknown(Guid testId)
_lastResult = null;
Text = Subject.Name;
public void StartedRunning(Guid testId)
_lastResult = null;
Text = Subject.Name + ” (Running)”;
The TestNode just changes its text and icon to reflect the status of the Test. Since we’ve made it clear (I hope) that we don’t want the test execution engine to “know” about the TreeView in the main screen, and the TestNode’s certainly don’t execute the Test’s themselves, something else has to tell the ITestObserver’s about Test events. The “hub” for publishing and subscribing to Test events in StoryTeller is an interface called ITestEventPublisher shown below:
[PluginFamily("Default", Scope = InstanceScope.Singleton)]
public interface ITestEventPublisher
void RegisterForTestEvents(ITestObserver observer, Test test);
void UnRegister(ITestObserver observer, Test test);
void PublishResult(Test test, TestResult result);
void MarkAsQueued(Test test);
void MarkAsUnknown(Test test);
void MarkAsExecuting(Test Test);
TestResult GetLastResult(Guid testId);
The [PluginFamily] attribute decorating the interface is just declarative wiring configuration for StructureMap (you don’t have to use attributes for configuration). Part of the StructureMap configuration is to only create a single instance of ITestEventPublisher for the entire AppDomain. Anytime another class retrieves ITestEventPublisher through service location or ITestEventPublisher is injected into a constructor function the same single instance will be returned. In effect, we’re just letting StructureMap handle the Singleton mechanics for us. It is, however, of the utmost importance to depend on the abstracted ITestEventPublisher interface rather than the concrete implementation (Chill out on the Singleton Fetish).
If you’ll note the UnRegister(ITestObserver, Test) method for a second. In this particular case I have reasons to explicitly unregister listeners. In normal usage I’m having each screen unregister itself when it’s closed to make sure that the garbage collection can reclaim the screen. Most people are taking advantage of WeakReference’s to solve the garbage collection problem (if the publisher keeps a reference to each observer, the observers can not be garbage collected).
You’ll also note that the RegisterForTestEvents(ITestObserver, Test) method explicitly requires you to specify the Test (the event subject) that the listener is interested in. In this case I’m making the concrete implementation of ITestEventPublisher responsible for remembering who’s listening to which tests. Otherwise, you could have the publisher broadcast messages to each subscriber and just assume that the subscribers will be responsible for ignoring irrelevant events. I’ve never liked that approach in real messaging infrastructure, but I don’t see it being that big of a difference inside a single process. In the StoryTeller case I thought that it made the implementation simpler to make the subscribers dumb.
Back to registering subscribers. Any class that listens for test execution events needs to register itself for each test with the an instance of ITestEventPublisher. The TestNode class registers itself in it’s constructor function by retrieving the configured instance of ITestEventPublisher from StructureMap and passing itself into the RegisterForTestEvents(ITestObserver, Test) method:
public TestNode(Test subject) : base(subject.Name, subject)
// Other setup that isn’t relevant to the Event Aggregation
// Grab the Event Aggregator out of StructureMap and register
// for any events related to the Subject of the node
ITestEventPublisher publisher = ObjectFactory.GetInstance<ITestEventPublisher>();
// Simple initialization
If you’ve been following Scott Bellware’s posts on dependencies, you’ll recognize ITestEventPublisher as a transitive dependency of the TestNode. Once TestNode registers itself with ITestEventPublisher there’s no more need to keep a reference around.
The internals of the concrete TestEventPublisher aren’t particularly that complicated. The registered subscribers are stored in a Dictionary like this**:
private Dictionary<Guid, List<ITestObserver>> _observers
= new Dictionary<Guid, List<ITestObserver>>();
The methods for raising events to the subscribers look like these two below. Just find the ITestObserver’s that are interested in a particular Test and fire the appropriate method for each subscriber.
public void MarkAsQueued(Test test)
test.Status = TestStatus.Queued;
foreach (ITestObserver observer in GetObservers(test))
public void MarkAsUnknown(Test test)
test.Status = TestStatus.Unknown;
foreach (ITestObserver observer in GetObservers(test))
Inside of the unit tests for TestEventPublisher I use RhinoMocks to test the interaction of the TestEventPublisher with the ITestObserver’s. The first set of unit tests just checks that TestEventPublisher is correctly associating ITestObserver’s with the proper Test subjects (I’ll spare you the details). After that I wrote tests like the following that verifies that the ITestObserver.Completed(TestResult) method is fired on the correct ITestObserver’s whenever ITestEventPublisher.PublishResult(Test, TestResult) is called.
private TestEventPublisher setupMockedPublisher()
// Using a Self-Mock for TestEventPublisher
// I’ve already unit tested the GetObservers(Test) method,
// so I’d like to remove that little wrinkle from the other tests
TestEventPublisher publisher = _mocks.CreateMock<TestEventPublisher>();
public void PublishResult()
TestEventPublisher publisher = setupMockedPublisher();
TestResult result = new TestResult();
foreach (ITestObserver observer in _observerArray)
Great, we’ve got an interface for subscribers and a unit tested hub (TestEventPublisher). Now all we need is to actually fire off the events from the publishers. Instead of opting for more indirection like the CAB, I just have the publishers call methods directly on the ITestEventPublisher interface. Again, I think it’s simpler and makes the code more intention revealing (the CTRL-B factor as well). If you want more decoupling you could have the hub listen to events on the publishers and then relay and/or transform the events to the subscribers.
One way or another, the publishers get the single instance of ITestEventPublisher from StructureMap. The presenter for the screen that let’s you edit a Test gets a reference from constructor injection.
public TestEditorPresenter(ITestView view, ITestFormatConverter converter, ITestEventPublisher publisher,
: base(view, converter)
_publisher = publisher;
_executor = executor;
The publisher I want to look at is called ExecutionEngine. Internally, it’s responsible for managing and coordinating the workflow of running tests. In this case ITestEventPublisher is a transparent dependency that ExecutionEngine interacts with throughout it’s lifetime.
public ExecutionEngine(ITestRunner runner, IComponentAnalyzer analyzers, ITestEventPublisher publisher)
_runner = runner;
_analyzers = analyzers;
_publisher = publisher;
Deep inside of ExecutionEngine it has a private method that actually executes a batch of tests.
private List<TestResult> executeTests(Test tests)
List<TestResult> list = new List<TestResult>();
foreach (Test test in tests)
Console.WriteLine(“Running Test “ + test.GetFullPath());
TestResult result = _runner.ExecuteTest(test);
Console.WriteLine(” “ + result.Counts.ToString());
ExecutionEngine calls the ITestEventPublisher.MarkAsExecuting(Test) method just before executing a Test and publishes the result afterwards with the ITestEventPublisher.PublishResult(Test, TestResult) method to let the subscribers know that an individual test is finished. This is important functionality. The StoryTeller tests can often be slow to execute because they’re usually integration tests, and it’s very handy to both give some UI cues about the progress and also allow the user to begin reviewing the test results while the remaining tests execute. It’s a huge advantage over the FitNesse Wiki tool that I’m trying to replace with StoryTeller.
Part of the unit test for running a batch of tests is shown below. I’m using RhinoMocks “ordered” mode to validate that the interactions happen in the correct order.
// Other stuff
// Other Stuff
So far I’d say that the TestEventPublisher design has worked out rather well. The one issue I didn’t mention was thread synchronization as the TestEventPublisher could easily be running in a background thread instead of the UI thread. I’ll leave that solution up to you dear reader;) On a serious note, do be aware of the thread synchronization issue here.
The rest of the code for the Event Aggregation in StoryTeller can be found in the Subversion trunk here: http://storyteller.tigris.org/svn/storyteller/trunk/src.
Besides the ever useful www.martinfowler.com site, check out these resources for different approaches to Event Aggregation:
The next post is a closely related followup to this post on the “Latch” strategy for controlling event rippling.
* Immediately following a successful project that had me working about 4 out of 5 weekends for almost six months. I’d say that it was a justified at work vacation except that it was mostly due to managerial malfeasance. My “official” and measurable output for about three months was a couple sentences in a current state analysis document. Almost verbatim: “Every process runs under the admin account. There is no security.”
** C# 3.0 type inference anyone? Seriously. I love the newer, more powerful language features of C# 2.0, but in some ways it’s been a huge step backwards in terms of code aesthetics. I’m feeling like C# 2.0 was really just the beta for 3.0.