Wed, November 24, 2004, 11:07 AM under MobileAndEmbedded
Here we will look at a solution for the bug reported previously. In a nutshell, we have a situation where we would like a menu to automatically appear, but instead the onus is passed onto our code. The solution is more interesting than the problem, and in fact in this case applies to other problems as well, so without further ado...

The initial (obvious) solution is to manually show a ContextMenu, which is easy given the Show method. This takes two arguments (the control and position) and is easy to call. The approach does indeed solve the problem and can be adequate for some scenarios, but is not without its problems. They all stem from the fact that when putting up a ContextMenu you are blocking: "This method does not return until the menu is dismissed".

Consider for example what happens if you are performing asynchronous operations (e.g. network requests) that are Control.Invoked to the GUI (e.g. populate a listview) and suddenly the user taps the ToolBarButton to get a menu and you use ContextMenu_Show: all those threads/messages are blocked in a queue only to be released (all at once!) when the menu is dismissed - I don't fancy that myself.

As a workaround to the aforementioned condition (and also a standalone requirement for some scenarios), you will want to stop other activities before showing the ContextMenu, and restarting them after it is dismissed. But wait, how do you know that they must be restarted when it is dismissed? The conditions/scenarios are different if it was dismissed due to a menu item being clicked (which as a result may have now navigated the user to different forms, for example) and different if the user tapped somewhere else, hence dismissing the ContextMenu. A solution to the new problem is to use a global flag tracking if a menu item was clicked or if the ContextMenu.Show call returned without a menu being clicked (and based on the state of the flag make the decision). As you can see, it gets messy.

Other differences compared to a plain menu include the following scenario: Form A shows form B; form B shows a ContextMenu; Form A decides (e.g. based on a Forms.Timer event) to close form B. The result is you are now looking at Form A while the ContextMenu from form B is still up! The workaround to this is to place a this.Capture = false in the Closing event of the Form.

Talking about the problems of manually showing ContextMenus, it is worth capturing (for the record) a known issue with showing them from the AfterSelect event of a TreeView. Rather than describe it here, I point you to the ng archives here and here [my posts there also include a workaround].

From my previous four paragraphs I hope you can see that ContextMenu.Show is useful and can solve some simple scenarios, but is definitelly not a one-to-one replacement for the user tapping on the arrow part of a ToolBarButton and having the menu auto-displayed for them (where none of the problems above apply).

Next time we look at a much nicer workaround to the original problem.
Comments are closed.