Google Search

Thursday, April 9, 2009

Creating customizable WPF drop-down menus (7)


Step 7: Using the new binding correctly


Remember that we overridden OnDragEnter so the menu will open upon hovering? Well, we will have to make a slight change to that code, to (also) use our new property:

protected override void OnDragEnter(DragEventArgs e)
{
    IsSubmenuOpen = true;
    IsSubmenuOpenInternal = true;
    e.Handled = true;
}

Great! Except now the menu never closes. Well, the first occasion we want it to close is when we're done dropping. So we will add the following snippet to our OnDrop code, just before the e.Handle = true code:
if (parentMenuItem != null)
    parentMenuItem.IsSubmenuOpenInternal = false;

Another case when we want it to close is whenever a neighbor menu opens, or in case we stopped using the menu at all (like, clicking any other place). But the thing is, we really don't want to write such code, as it is already written. We need a way to find when the original property would change to false and do the same.
Luckily, there is a routed event just for that: SubmenuClosed.
We will register to that event, and change our new property to false likewise. Except, we do it with a minor change: we need to make sure we are not in the middle of a drag operation, or right in the drop handling code. We will register to that event in the constructor, and implement it:
public DraggableMenuItem()
{
    AllowDrop = true;
    SubmenuClosed += new RoutedEventHandler(DraggableMenuItem_SubmenuClosed);
}

void DraggableMenuItem_SubmenuClosed(object sender, RoutedEventArgs e)
{
    if (Mouse.LeftButton == MouseButtonState.Pressed || !IsDragging)
        IsSubmenuOpenInternal = false;
}

But now there is another problem - the menu doesn't open well, since we replaced the IsSubmenuOpen with IsSubmenuOpenInternal. So the same way, we need to handle the open event - SubmenuOpened:
public DraggableMenuItem()
{
    AllowDrop = true;
    SubmenuClosed += new RoutedEventHandler(DraggableMenuItem_SubmenuClosed);
    SubmenuOpened += new RoutedEventHandler(DraggableMenuItem_SubmenuOpened);
}

void DraggableMenuItem_SubmenuOpened(object sender, RoutedEventArgs e)
{
    IsSubmenuOpenInternal = true;
}

And that's it! The menu is now Drag-and-Drop customizable! Note that the code brought here in this article is for explanatory purposes only, and is far from perfect. Its goal is to explain the main issues and problems encountered while developing such a control, so use it as a reference only.

Hope you have found it useful, and please let me know if you did, and I'll be glad to hear about successful (preferably complete) implementations.

1 comment: