Mouse vs Keyboard and ContextMenus

Recently I was asked to modify one of my controls so the user of the control could get the proper placement of thier context menus.  My intial response was for them to use the placement properties on the ContextMenu/ContextMenuService to achieve the right positioning.

Then the real issue came out.  They wanted different positioning based on how the context menu was opened.  If opened via a right mouse click then the placement is supposed to be the MousePoint.  If opened via the keyboard then it was to be Bottom.

This behavior has to be implemented as logic in code, rather than as simply specifing the settings in XAML.

Well we worked out the situation to everyone's satisfaction, but my mind wouldn't let it rest.  The 2 questions which kept bugging me were:  "This seems like standard behavior.  Why isn't this the default?" and second "Why isn't this situation supported via XAML?"

So I contacted the WPF team.  The first now has a bug filed and the second is on thier list of feature requests.  Now I can wait for .NET 4.0 and hope that these issues were addressed, or I could do something now.

So I came up with this idea to have an attached service which will allow me to specify different placement settings in XAML for the keyboard vs mouse scenerio.

This is how it works.  I declared a class called ContextMenuPlacement.  It inherits from FrameworkElement to support Data Binding and has properties for Placement, PlacementTarget, etc...  You know all those interesting ones from ContextMenuService.

Then I declared 2 attached properties, KeyboardPlacement and MousePlacement which are both of type ContextMenuPlacement.  Now we can associate 2 ContextMenuPlacement objects with a given DependencyObject.

 Now here is the cool part.  The property changed handler for the 2 properties subscribe to the ContextMenuOpening event on the object the properties are attached to.  In the handler for the ContextMenuOpening event we determine if the context menu is opening via mouse or keyboard.  Then we get the right ContextMenuPlacement object, copy it's settings into the attached ContextMenuService attached properties on the object.  Then we let the context menu open.

 Now in XAML we can specify different placement settings.  Complete code is attached.

I did hit one wrinkle - which is what is the DataContext for the ContextMenuPlacement objects?  Without that it makes it hard to bind properties like PlacementTarget to anything useful.  So to work around this I set the DataContext to the context menu target.  Then the binding works.  Perhaps someone knows a better way to handle this problem.

 NOTE: The attached code is meant to illustrate an idea and is not quality production code.  Use at your own risk.

 

ContextMenuTest.zip