Wow, an even dozen, and I’ve still got a ways to go. Just to prove that I can write a short post, this one is brief (because it was meant to be a little section in the Event Aggregator post).
A couple people have asked for a PDF of the series when it’s done. I’ll do my best to stitch it together and just slap it up on CodeBetter somewhere. In the meantime, here’s the predecessor posts in the series:
- Preamble
- 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
- Rein in runaway events with the “Latch” (This post)
- Embedded Controllers – Forthcoming
- MicroControllers – Forthcoming
- Subcutaneous Testing – Forthcoming
- Creating the Application Shell – probably a couple posts
- Wiring the Components with an IoC tool – Forthcoming
The “Latch”
In the last post I talked a little bit about runaway events in a fat client. One event causes an action that changes another control which conceivably fires another event which eventually sends you application spiraling out of control. All because a little bug went Kachhhoooooo! What we need to do is ignore or turn off an event handler while a certain action is taking place. You could just temporarily detach the event handler while you’re performing that action like this code:
// Remove the event handler for the moment
_someComboBox.SelectedIndexChanged -= new System.EventHandler(someHandler);
// do something that would probably make _someComboBox fire the SelectedIndexChanged event
// Put the event handler back
_someComboBox.SelectedIndexChanged += new System.EventHandler(someHandler);
It works, but I wouldn’t want to do it everywhere. It’s tightly coupling the code inside this method with the rest of the View. I would bet that this approach would quickly make a View very difficult to modify.
My preferred approach is what I call a “Latch” (it’s taken from a pattern used in messaging to stop a message from endlessly cycling between two systems that publish events to each other). Inside any method or logical operation that could cause cascading events you simply set the latch to mark an operation in progress. In the relevant event handlers you first check to see if the latch is set, and cancel any additional actions in the event handler is the latch is indeed set. It could be as simple as just tracking a Boolean field in a View class. Slightly more sophisticated is something like the “Latch” class from StoryTeller:
public delegate void VoidHandler();
public class Latch
{
private int _count = 0;
public void Increment()
{
_count++;
}
public void Decrement()
{
_count–;
}
public bool IsLatched
{
get { return _count > 0; }
}
public void RunInsideLatch(VoidHandler handler)
{
Increment();
handler();
Decrement();
}
public void RunLatchedOperation(VoidHandler handler)
{
if (IsLatched)
{
return;
}
handler();
}
There isn’t too much to the usage. In the method that performs work you might do this:
_latch.RunInsideLatch(delegate
{
// The actions that spawn cascading events
activatePresenter(presenter, page);
_tabControl.Items.Add(page);
_tabControl.SelectedTab = page;
}
);
In an event handler effected by this work you could guard the event propagation by doing this
void TabControl_TabSelected(object sender, TabEventArgs args)
{
if (_latch.IsLatched)
{
return;
}
ContentTab tab = (ContentTab) TabControl.SelectedTab;
activatePresenter(tab.Presenter, tab);
}
or this version (fun with anonymous delegates):
void TabControl_TabSelected(object sender, TabEventArgs args)
{
_latch.RunLatchedOperation(
delegate
{
ContentTab tab = (ContentTab)TabControl.SelectedTab;
activatePresenter(tab.Presenter, tab);
});
}
I’ve used this pattern about 3-4 times with some success. It’s especially powerful in conjunction with an Event Aggregator.
So what did I really buy here? In the application, opening a new screen involves the creation and activation of a new TabControl, which raises an event for selecting a new tab. For tabs that are just inactive, I need an indication from the SelectedTab event of a TabControl to tell that particular Presenter to reactivate. The SelectedTab event is necessary for screens that are already open, but nothing but trouble for creating a brand new Tab. By setting a latch in the event that opens a new TabControl, I an easily ignore the SelectedTabl event just while the TabControl is being setup.
Is this really a design pattern?
I’ve used some derivation of this solution on at least four projects, so it’s safe to say that it is *a* pattern. Design patterns generally aren’t anything terribly fancy, there just things that you do – repeatedly. When experienced people learn design patterns I often hear some snorting to the effect of “I already do this.” Yes, that’s kind of the point in calling it a “pattern.”
I’m not an officially sanctioned pattern naming body, so it’s not necessarily appropriate for me to be making up the name here. I’d bet anything that someone else has already described this pattern and I’m just not familiar with the named pattern. If you’ve seen this written up somewhere else, please throw in a link in the comments.