views:

90

answers:

1

I have a Menu and Submenu structure in Silverlight, and I want the submenu to disappear when the parent menu item loses focus - standard Menu behavior. I've noticed that the submenu's click events are lost when a submenu item is clicked, because the parent menu item loses focus and the submenu disappears.

It's easier to explain with code:

        ParentMenuBtn.Click += delegate
        {
            SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? SubMenu.Collapsed : SubMenu.Visible;
        };
        ParentMenuBtn.LostFocus += delegate
        {
            SubMenu.Visibility = Visibility.Collapsed;
        };
        SubMenuBtn.Click += delegate
        {
            throw new Exception("This will never be thrown.");
        };

In my example, when SubMenuBtn is clicked, the first event that triggers is ParentMenuBtn.LostFocus(), which hides the container of SubMenuBtn. Once the container's visibility collapses, the Click event is never triggered.

I'd rather avoid having to hide the sub-menu each time, but I'm a little surprised that the Click event is never triggered as a result...

I can't put any checks inside the LostFocus() event to see if my SubMenuBtn has focus, because it does not gain focus until after the LostFocus() event is called. In other words, SubMenuBtn.IsFocused = false when LostFocus() is triggered.

Anyone have any thoughts about this?

A: 

I've found out the solution - albeit, it's not as simple, or elegant as I would have liked. The solution is to use a secondary thread that pauses only for a moment before executing.

ie.

public partial class Navigation : UserControl
{
    public Navigation()
    {
        ParentMenuBtn.Click += delegate
        {
            SubMenu.Visibility = (SubMenu.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;    
        };
        ParentMenuBtn.LostFocus += delegate(object sender, RoutedEventArgs e)
        {
            HideSubMenu(SubMenu);
        };
        SubMenuBtn.Click += delegate
        {
            //Sub Menu Button actions...
        };

    private void HideSubMenu(UIElement subMenu)
    {
        //Get the Main Page
        App app = (App)Application.Current;
        MainPage mainPage = (MainPage)app.RootVisual;

        Thread thread = new Thread(Navigation.HideSubMenu);
        thread.Start(new ThreadState(mainPage, subMenu));
    }

    private static void HideSubMenu(object threadStateObj)
    {
        ThreadState threadState = (ThreadState)threadStateObj;
        //Execute after 5 milliseconds...
        System.Threading.Thread.Sleep(5);
        threadState.MainPage.Dispatcher.BeginInvoke(delegate() {
           threadState.TargetElement.Visibility = Visibility.Collapsed;
        });
    }

I just use a simple object called ThreadState to handle all the state objects I want to preserve:

public class ThreadState
{
    public MainPage MainPage = null;
    public UIElement TargetElement = null;

    public ThreadState(MainPage mainPage, UIElement targetElement)
    {
        this.MainPage = mainPage;
        this.TargetElement = targetElement;
    }
}
AlishahNovin