One of the cool things about being a software developer is the chance to learn something new every day, even if it’s something small. I always get a little rush when I discover something I didn’t previously know.
The other day I was refactoring some code and ran up against an interesting bug. A WorkItem that I had created for a Use Case was responsible for creating several views in a DockWorkspace, and then exposing that view to another other WorkItem that wanted to create and use it, so that it could be shown in a WindowWorkspace, for example.
But a WindowWorkspace can be a tricky bugger; if the user clicks the Close button (”X”) in the upper right hand corner, the Form closes and disposes of the view.
Well… sometimes.
The WorkItem I created was wired-up to the view’s Disposing event to see when it was being disposed. Because when the view was disposed, the WorkItem was then in an invalid state - it’s fairly useless without the view - and in that case, I simply wanted the WorkItem to terminate, because it meant the user had clicked the “X” and closed that particular Use Case.
This WorkItem was used in two locations. In one location the WorkItem was created and it’s main view shown in a WindowWorkspace. When the user clicked the “X” button to close the Form, the WorkItem caught the Disposing event and Terminated correctly. The user could then re-open that WorkItem.
In another location, similar code (so it seemed) was doing the same job: creating the WorKItem and showing its view in a WindowWorkspace. But when the user clicked the “X” to close the Form, the view didn’t get Disposed and the WorkItem didn’t Terminate. When the user when to re-open that WorkItem the application would throw an exception because it already existed in the WorkItem collection hierarchy. This was unexpected.
I scratched my head for a few minutes trying to figure out why these two pieces of code that seemed to be using this WorkItem identically were functioning differently. It turned out that in one case, when the WorkItem’s view was shown, it was shown in the WindowWorkspace in a Modal fashion. In the other case, it was shown in a non-modal fashion. Both cases had very good reasons for showing them the way they did, so we couldn’t simply make them both the same. Plus, I wanted to know why I was seeing this behavior…
Upon further digging I found this on MSDN:
When a form is closed, it is disposed, releasing all resources associated with the form. If you cancel this event, the form remains opened.
That is the behavior I expected, and indeed verified with the first case. But then there’s this neat little note:
When a form is displayed as a modal dialog box, clicking the Close button (the button with an X at the upper-right corner of the form) causes the form to be hidden and the DialogResult property to be set to DialogResult.Cancel.
The Form is not Disposed…
Now, obviously this makes perfect sense. When you’re working with traditional Windows Forms you’re used to this behavior because you typically fetch a DialogResult back, and a Disposed object can’t send back a DialogResult. But when you get your mind attuned to the way CAB does things, sometimes, well, you forget :)