Google Search

Thursday, April 9, 2009

Creating customizable WPF drop-down menus (6)


Why dropping doesn't work on sub-menus?


This question is actually the heart of the entire process. All we did until now was quite straight-forward.
So, why doesn't it work? To solve that I tried several debugging methods, but the one that really got me somewhere was to enable Reference Code Debugging.
So I found myself debugging deep into the WPF code, finding that during the drop operation, the result of the hit-test being performed is actually 'null'(!). More than that, it seemed like the native window holding the sub-menu wasn't even there (!!). How can that be?
Well, I couldn't debug much deeper than that, but thinking about how drop-down menus work, and how drag-and-drop mechanisms work, I figured that the problem must be that the sub-menu popup was closing before the drop operation hit test takes place.

This means that if I want to be able to drop things on a sub-menu, I need to build a new mechanism that will keep it open until AFTER the drop.

Step 6: Keeping the sub-menu open


Now it is going to get clear why I insisted on inheriting MenuItem (and be sure it will get worse, as we will need to change the default control template).

We will need to introduce a new Dependency Property that will enable us to better control whenever the sub-menu is open. Let's call this property IsSubmenuOpenInternal:
public static readonly DependencyProperty IsSubmenuOpenInternalProperty =
   DependencyProperty.Register("IsSubmenuOpenInternal",
                               typeof(bool), typeof(DraggableMenuItem),
                               new FrameworkPropertyMetadata(false));

public bool IsSubmenuOpenInternal
{
   get { return(bool)GetValue(IsSubmenuOpenInternalProperty); }
   set { SetValue(IsSubmenuOpenInternalProperty, value); }
}

In the XAML code, we will change the default style (this can be easily done using "Expression Blend") so the sub-menu opening state will now bind to our new property, rather than the default one.

I will not place here the entire XAML for the new menu item class, since it is just too long.
What you will need to do is to use Expression Blend to copy the default MenuItem style, then make the following changes:
  • Change the TargetType attribute to our new class type. Remember to add the relevant namespace declaration in the XAML declaration.
  • Locate the Popup element block, and change its IsOpen binding from IsSubmenuOpen to our new IsSubmenuOpenInternal dependency property.



Important Note:
If I could, I would of course rather override the default IsSubmenuOpen property, leaving the XAML as it is. However, this is impossible (at least in accepted methods, without using reflection), so adding this property really seems like the only way to go.


Now that the new bindings are in place, we can move on to the next post :-)

1 comment:

  1. Hi,

    I have changed IsOpen binding from IsSubmenuOpen to new IsSubmenuOpenInternal dependency property. But my SubMenu is not getting opened even once. Can you please upload the whole sample?

    ReplyDelete