Skip to content

asynchronous call to RibbonMenuItem.UpdateDropDownPosition throwing InvalidOperation #10976

@trebahl

Description

@trebahl

Description

RibbonMenuItem.UpdateDropDownPosition is launched asynchronously (BeginInvoke) from RibbonMenuItem.OnIsSubmenuOpenChanged. Suppose an event handler plugged on SubmenuOpened closes the sub menu. Then the effective call to UpdateDropDownPosition will happen after the menu has been closed. In that case, an InvalidOperationException will be thrown by Visual.PointToScreen called from UpdateDropDownPosition because it makes no sense on a window that is off screen.

I'm aware this looks like an obscure corner case, but I stumbled upon it quite naturally while making a dynamic submenu whose items are regenerated at every opening: if the generated list is empty, I just close the menu. I can work around this by closing the menu asynchronously, except I get a flickering.

Would it be possible to add a check at the start of UpdateDropDownPosition to do nothing if IsSubmenuOpen is false?

Reproduction Steps

    public void RibbonSubMenuCrash()
    {
        var d = new RibbonMenuButton() { Label = "DD" }; // drop down button in ribbon
        var it = new RibbonMenuItem() { Header = "item with submenu" }; // menu item with a sub item inside d
        d.Items.Add(it); 
        it.Items.Add(new RibbonMenuItem()); 

        // event handler to close it's submenu; sounds pointless but in my real use case it is only closing in some specific circumstances
        it.SubmenuOpened += (o, e) =>
        {
            d.IsDropDownOpen = false; // causes a crash in the last doevents
        };


        var w = new RibbonWindow();
        var r = new System.Windows.Controls.Ribbon.Ribbon();
        w.Content = r;
        var t = new RibbonTab();
        r.Items.Add(t);
        var g = new RibbonGroup();
        t.Items.Add(g);
        g.Items.Add(d);

        w.Show();

        // simulates opening d, then attempting to open the sub menu 
        Action doevents = () => Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
        doevents();
        d.IsDropDownOpen = true;
        doevents();
        it.IsSubmenuOpen = true;
        doevents(); // invalidoperationexception thrown from async call to RibbonMenuItem.UpdateDropDownPosition
    }

Expected behavior

sub menu not appearing

Actual behavior

Exception is thrown.

Regression?

No response

Known Workarounds

Closing the submenu asynchronously:
Dispatcher.CurrentDispatcher.BeginInvoke(() => d.IsDropDownOpen = false);

Impact

Can't have a dynamic sub menu without flickering.

Configuration

.Net 8
window 10 x64
Most likely not system specific as the reason for the crash is quite clear from the code.

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    InvestigateRequires further investigation by the WPF team.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions