amwx / FluentAvalonia

Control library focused on fluent design and bringing more WinUI controls into Avalonia
MIT License
1.05k stars 97 forks source link

Exception when trying to remove selected item in NavigationView 2.1.0-preview5 #572

Closed OleRoss closed 5 months ago

OleRoss commented 6 months ago

Describe the bug

When trying to remove a NavigationViewItem with a dynamic MenuItemsSource, while the Item is selected, I get an exception:

System.Exception: Cannot run layout in the middle of a collection change
   at FluentAvalonia.UI.Controls.ItemsRepeater.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Controls.Presenters.ScrollContentPresenter.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at Avalonia.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean& hasDesiredSizeUChanged)
   at Avalonia.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at Avalonia.Controls.Grid.MeasureOverride(Size constraint)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureOverride(Size availableSize)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Controls.Grid.MeasureCell(Int32 cell, Boolean forceInfinityV)
   at Avalonia.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV, Boolean& hasDesiredSizeUChanged)
   at Avalonia.Controls.Grid.MeasureCellsGroup(Int32 cellsHead, Size referenceSize, Boolean ignoreDesiredSizeU, Boolean forceInfinityV)
   at Avalonia.Controls.Grid.MeasureOverride(Size constraint)
   at Avalonia.Layout.Layoutable.MeasureCore(Size availableSize)
   at Avalonia.Layout.Layoutable.Measure(Size availableSize)
   at Avalonia.Layout.LayoutManager.Measure(Layoutable control)
   at Avalonia.Layout.LayoutManager.Measure(Layoutable control)
   at Avalonia.Layout.LayoutManager.Measure(Layoutable control)
   at Avalonia.Layout.LayoutManager.Measure(Layoutable control)
   at Avalonia.Layout.LayoutManager.ExecuteMeasurePass()
   at Avalonia.Layout.LayoutManager.InnerLayoutPass()
   at Avalonia.Layout.LayoutManager.ExecuteLayoutPass()
   at Avalonia.Layout.Layoutable.UpdateLayout()
   at FluentAvalonia.UI.Controls.ViewportManager.OnBringIntoViewRequested(RequestBringIntoViewEventArgs args)
   at FluentAvalonia.UI.Controls.ItemsRepeater.OnBringIntoViewRequested(Object sender, RequestBringIntoViewEventArgs args)
   at Avalonia.Interactivity.Interactive.<AddHandler>g__InvokeAdapter|4_0[TEventArgs](Delegate baseHandler, Object sender, RoutedEventArgs args)
   at Avalonia.Interactivity.Interactive.<>c__4`1.<AddHandler>b__4_1(Delegate baseHandler, Object sender, RoutedEventArgs args)
   at Avalonia.Interactivity.EventRoute.RaiseEventImpl(RoutedEventArgs e)
   at Avalonia.Interactivity.EventRoute.RaiseEvent(Interactive source, RoutedEventArgs e)
   at Avalonia.Interactivity.Interactive.RaiseEvent(RoutedEventArgs e)
   at Avalonia.Controls.ControlExtensions.BringIntoView(Control control, Rect rect)
   at Avalonia.Controls.ControlExtensions.BringIntoView(Control control)
   at Avalonia.Controls.ScrollViewer.OnGotFocus(GotFocusEventArgs e)
   at Avalonia.Input.InputElement.<>c.<.cctor>b__32_0(InputElement x, GotFocusEventArgs e)
   at Avalonia.Interactivity.RoutedEvent`1.<>c__DisplayClass1_0`1.<AddClassHandler>g__Adapter|0(Object sender, RoutedEventArgs e)
   at Avalonia.Interactivity.RoutedEvent.<>c__DisplayClass23_0.<AddClassHandler>b__0(ValueTuple`2 args)
   at Avalonia.Reactive.AnonymousObserver`1.OnNext(T value)
   at Avalonia.Reactive.LightweightObservableBase`1.PublishNext(T value)
   at Avalonia.Reactive.LightweightSubject`1.OnNext(T value)
   at Avalonia.Interactivity.RoutedEvent.InvokeRaised(Object sender, RoutedEventArgs e)
   at Avalonia.Interactivity.EventRoute.RaiseEventImpl(RoutedEventArgs e)
   at Avalonia.Interactivity.EventRoute.RaiseEvent(Interactive source, RoutedEventArgs e)
   at Avalonia.Interactivity.Interactive.RaiseEvent(RoutedEventArgs e)
   at Avalonia.Input.KeyboardDevice.SetFocusedElement(IInputElement element, NavigationMethod method, KeyModifiers keyModifiers)
   at Avalonia.Input.FocusManager.Focus(IInputElement control, NavigationMethod method, KeyModifiers keyModifiers)
   at Avalonia.Input.InputElement.Focus(NavigationMethod method, KeyModifiers keyModifiers)
   at FluentAvalonia.UI.Controls.ViewManager.MoveFocusFromClearedIndex(Int32 clearedIndex)
   at FluentAvalonia.UI.Controls.ViewManager.ClearElementToElementFactory(Control element)
   at FluentAvalonia.UI.Controls.ViewManager.ClearElement(Control element, Boolean isClearedDueToCollectionChange)
   at FluentAvalonia.UI.Controls.ItemsRepeater.ClearElementImpl(Control element)
   at FluentAvalonia.UI.Controls.RepeaterLayoutContext.RecycleElementCore(Control element)
   at FluentAvalonia.UI.Controls.VirtualizingLayoutContext.RecycleElement(Control element)
   at FluentAvalonia.UI.Controls.ElementManager.ClearRealizedRange(Int32 realizedIndex, Int32 count)
   at FluentAvalonia.UI.Controls.ElementManager.ClearRealizedRange()
   at FluentAvalonia.UI.Controls.ElementManager.DataSourceChanged(Object source, NotifyCollectionChangedEventArgs args)
   at FluentAvalonia.UI.Controls.FlowLayoutAlgorithm.OnItemsSourceChanged(Object source, NotifyCollectionChangedEventArgs args, VirtualizingLayoutContext context)
   at FluentAvalonia.UI.Controls.StackLayout.OnItemsChangedCore(VirtualizingLayoutContext context, Object source, NotifyCollectionChangedEventArgs args)
   at FluentAvalonia.UI.Controls.ItemsRepeater.OnItemsSourceViewChanged(Object sender, NotifyCollectionChangedEventArgs args)
   at FluentAvalonia.UI.Controls.FAItemsSourceView.OnItemsSourceChanged(NotifyCollectionChangedEventArgs args)
   at FluentAvalonia.UI.Controls.FAItemsSourceView.OnCollectionChanged(NotifyCollectionChangedEventArgs args)
   at FluentAvalonia.Core.SimpleObserver`1.OnNext(T value)
   at Avalonia.Reactive.LightweightObservableBase`1.PublishNext(T value)
   at Avalonia.Collections.NotifyCollectionChangedExtensions.WeakCollectionChangedObservable.OnEvent(Object sender, WeakEvent ev, NotifyCollectionChangedEventArgs e)
   at Avalonia.Utilities.WeakEvent`2.Subscription.OnEvent(Object sender, TEventArgs eventArgs)
   at Avalonia.Utilities.WeakEvents.<>c__DisplayClass5_0.<.cctor>b__5(Object _, NotifyCollectionChangedEventArgs e)
   at System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)
   at AvaloniaTests.ViewModels.MainWindowViewModel.Test() in ...\AvaloniaTests\AvaloniaTests\ViewModels\MainWindowViewModel.cs:line 16
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state)
   at Avalonia.Threading.SendOrPostCallbackDispatcherOperation.InvokeCore()
   at Avalonia.Threading.DispatcherOperation.Execute()
   at Avalonia.Threading.Dispatcher.ExecuteJob(DispatcherOperation job)
   at Avalonia.Threading.Dispatcher.ExecuteJobsCore(Boolean fromExplicitBackgroundProcessingCallback)
   at Avalonia.Threading.Dispatcher.Signaled()
   at Avalonia.Win32.Win32DispatcherImpl.DispatchWorkItem()
   at Avalonia.Win32.Win32Platform.WndProc(IntPtr hWnd, UInt32 msg, IntPtr wParam, IntPtr lParam)
   at Avalonia.Win32.Interop.UnmanagedMethods.DispatchMessage(MSG& lpmsg)
   at Avalonia.Win32.Win32DispatcherImpl.RunLoop(CancellationToken cancellationToken)
   at Avalonia.Threading.DispatcherFrame.Run(IControlledDispatcherImpl impl)
   at Avalonia.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at Avalonia.Threading.Dispatcher.MainLoop(CancellationToken cancellationToken)
   at Avalonia.Controls.ApplicationLifetimes.ClassicDesktopStyleApplicationLifetime.Start(String[] args)
   at Avalonia.ClassicDesktopStyleApplicationLifetimeExtensions.StartWithClassicDesktopLifetime(AppBuilder builder, String[] args, Action`1 lifetimeBuilder)
   at AvaloniaTests.Program.Main(String[] args) in ...\AvaloniaTests\AvaloniaTests\Program.cs:line 13

Screenshots

Desktop/Platform (please complete the following information):

Additional context The code throws somewhere deep down when trying to remove the focus from the element, I think. Therefore I could create a workaround where I manually selected a different menu item before removing the old one, but it would be great if that would not be necessary.

Note to the minimal repo:

OleRoss commented 5 months ago

I will close this issue as the error is not happening anymore. However, please see #588 as I get an other exception when executing the minimal repo.

Thanks for the work!