asynchronous call to `RibbonMenuItem.UpdateDropDownPosition` throwing InvalidOperation
- Dominant language
- C#
- Stars
- 7.7k
- Forks
- 1.3k
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 61
Description
### 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_
Contributor guide
Assessment
This issue has not been assessed yet.