This is an excerpt from my book draft (written, oh, 10 minutes ago) about how to deal with having a hidden screen temporarily disregard messages from the Event Aggregator while it is hidden. I do know that the spacing is wacky, and I apologize in advance. Some quick terminology:
- Screen Conductor – This is what I’m tentatively calling the pattern for a class that acts as a mediator between the various screens in the application and controls the screen activation lifecycle. In some apps it would just be the Application Controller, in other apps you might want to pull it into a separate class as the Application Controller gets big
- Screen Activator – Until I or someone else comes up with a better name, this is the responsibility for activating, deactivating, or tearing down a single screen. I’ll write much more about this early next week.
For performance reasons you may want to have subscribers temporarily ignore events from the Event Aggregator. A couple years back I worked on an application that presented visualizations of market data inside a financial reporting application. The application would show different views of the same market data in different tabs within the application shell. The market data would occasionally update on the backend and trigger the user interface to update. The problem was that the act of performing the data visualization was quite expensive – and making the user wait while hidden screens were also updated probably made the application frustratingly sluggish to use. In order to improve the user experience, the designers of this application “latched” the inactive screens so that they would not automatically update for new data until they were later displayed when the user selected other tabs.
So let’s say you are building a system with a similar scenario where screens need to potentially refresh the visualization of some sort of market data when the market moves.
// Subscribers would receive the “Market Data Changed”
// event with this interface
public interface IMarketDataListener
{
void Update(MarketDataChanged message);
}
How would you go about latching the reaction to the MarketDataChanged event when a screen is hidden? The first thing I think I would do is worry about how the individual screens “know” whether they are shown or not. This is a case where I think you really want to be using the combination of Screen Conductor and the Screen Activator [LINK] to be managing the activation and deactivation of screens. Let’s say that we’ve adopted a very simple implementation of these patterns:
public interface IScreen
{
void Activate();
void Deactivate();
}
public class SimpleScreenConductor
{
private IScreen _currentScreen;
public void ActivateScreen(IScreen newScreen)
{
_currentScreen.Deactivate();
newScreen.Activate();
_currentScreen = newScreen;
}
}
The Activate() and Deactivate() methods “tell” an individual IScreen that it is turned on or off in the visual display and give each IScreen a chance to start or stop screen behaviors. When a new screen is activated or an existing screen in the application is made the active screen, the SimpleScreenConductor will first tell the currently open screen to deactivate itself, and then activate the newly active screen.
Now that we have a way to let our market data visualization screens know when they are actively shown or hidden, we need to latch the market data changed events in the inactive case – with one wrinkle. When the use does activate the inactive visualizations later, those screens need to refresh themselves for the new data. A sample implementation of a LatchedPresenter is shown below:
public class LatchedPresenter : IScreen, IMarketDataListener
{
private readonly IMarketDataRepository _repository;
private bool _needToRefresh;
private bool _latched;
public LatchedPresenter(IMarketDataRepository repository)
{
_repository = repository;
}
public void Activate()
{
_latched = false;
if (_needToRefresh)
{
updateVisualization();\
_needToRefresh = false;
}
}
public void Deactivate()
{
_latched = true;
}
public void Update(MarketDataChanged message)
{
if (_latched)
{
_needToRefresh = true;
}
else
{
updateVisualization();
}
}
private void updateVisualization()
{
// call to the IMarketDataRepository to get
// the new data and refresh the visualization
// of the market data
}
}
When the LatchedPresenter receives a “MarketDataChanged” event, it first checks to see if it is “latched.” If the events are latched it simply remembers that it needs to update itself upon the next activation by setting the field _needToRefresh to true. If it is not latched, LatchedPresenter will update the visualization immediately. Likewise, when LatchedPresenter is activated again it will check the _needToRefresh field. If that field is true LatchedPresenter will update the visualization.
The performance benefit is obvious. With latching the hidden screens are not consuming computing resources to update visualizations that might not ever be viewed by the financial analyst using the application.