Showing non-user code in Call Stack
I've run into a couple of weird Windows Forms problems today. In VS .NET 2002, when I used to bring up the call stack on an event handler (”OnWhatever...”), I used to see all of the Framework calls that got me from the last call in my own code to the call I'm actually debugging. Although you can't see the code behind these calls (they're compiled and you have no debug info), they're often useful for getting a hint about what's happening in the Framework assembly by reading the method names.
For example, in the current situation I'm, I've inherited from ListView. I'm overriding OnItemCheck because I want to be told when the user changes the item checks, but I don't want to run the routine that updates the underlying data too often because it involves all sorts of expensive operations. What I've done in my code is set a flag when I think that the check could possibly change - i.e. when I'm programaticlaly setting them when adding them to the list. I check the flag in OnItemCheck and discard any calls that I think are happening in response to me programmatic changes.
However, in between me adding the items to the list and the list being displayed, the OnItemCheck method is being called at some point. That's the reason why hiding non-user code is a problem - I need to ignore OnItemCheck's ideally in all situations when the user is not physically clicking on a check box.
In VS .NET 2003, by default if the call stack contains non-user code (i.e. code running in assemblies that don't have debug info available), the Call Stack window “helpfully” collapses this down to a single line “<Non-user code>”. In effect, this turns what happened between leaving your own code and re-entering your own code is a complete mystery. To solve this problem, right-click on the Call Stack window and check on “Show non-user code”.
With the non-user code visible, I can very quickly see that the OnItemCheck is being called at some point after a call to OnHandleCreated. I can't see what the ListView class is doing (well, I could if I decompiled the thing.), but there is a clue there. Indeed, if I override OnHandleCreated and add set my “ignore” flag before calling the base implemention and clear it afterwards, I get the information I need.