Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Putting RoutedViewHost inside a TabControl on Avalonia causes an app crash after navigating the second time #3680

Closed
kyurkchyan opened this issue Nov 24, 2023 · 7 comments
Labels

Comments

@kyurkchyan
Copy link

kyurkchyan commented Nov 24, 2023

Describe the bug 🐞

I have the following scenario

  1. A TabControl with 2 tabs - Companies and Employees
  2. Each tab has a RoutedViewHost
  3. When the app is launched everything is rendered correctly
  4. When you go from companies tab to the employees and come back to companies tab the application crashes with the following error
Unhandled exception. System.InvalidOperationException: The control already has a visual parent.
   at Avalonia.Visual.ValidateVisualChild(Visual c)
   at Avalonia.Collections.AvaloniaList`1.Add(T item)
   at Avalonia.Controls.Presenters.ContentPresenter.UpdateChild(Object content)
   at Avalonia.Controls.Presenters.ContentPresenter.ContentChanged(AvaloniaPropertyChangedEventArgs e)
   at Avalonia.Animation.Animatable.OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
   at Avalonia.AvaloniaObject.RaisePropertyChanged[T](AvaloniaProperty`1 property, Optional`1 oldValue, BindingValue`1 newValue, BindingPriority priority, Boolean isEffectiveValue)
   at Avalonia.PropertyStore.EffectiveValue`1.SetAndRaiseCore(ValueStore owner, StyledProperty`1 property, T value, BindingPriority priority, Boolean isOverriddenCurrentValue, Boolean isCoercedDefaultValue)
   at Avalonia.PropertyStore.ValueStore.SetLocalValue[T](StyledProperty`1 property, T value)
   at Avalonia.PropertyStore.ValueStore.SetValue[T](StyledProperty`1 property, T value, BindingPriority priority)
   at Avalonia.Controls.Presenters.ContentPresenter.set_Content(Object value)
   at Avalonia.Controls.TransitioningContentControl.UpdateContent(Boolean withTransition)
   at Avalonia.Controls.TransitioningContentControl.OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
   at Avalonia.Visual.OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
   at Avalonia.Layout.Layoutable.OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
   at Avalonia.Controls.Control.OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
   at Avalonia.Visual.SetVisualParent(Visual value)
   at Avalonia.Visual.SetVisualParent(IList children, Visual parent)
   at Avalonia.Collections.AvaloniaList`1.NotifyAdd(T item, Int32 index)
   at Avalonia.Controls.Presenters.ContentPresenter.UpdateChild(Object content)
   at Avalonia.Controls.Presenters.ContentPresenter.ContentChanged(AvaloniaPropertyChangedEventArgs e)
   at Avalonia.Animation.Animatable.OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change)
   at Avalonia.AvaloniaObject.RaisePropertyChanged[T](AvaloniaProperty`1 property, Optional`1 oldValue, BindingValue`1 newValue, BindingPriority priority, Boolean isEffectiveValue)
   at Avalonia.PropertyStore.EffectiveValue`1.SetAndRaiseCore(ValueStore owner, StyledProperty`1 property, T value, BindingPriority priority, Boolean isOverriddenCurrentValue, Boolean isCoercedDefaultValue)
   at Avalonia.PropertyStore.EffectiveValue`1.SetAndRaise(ValueStore owner, IValueEntry value, BindingPriority priority)
   at Avalonia.PropertyStore.ValueStore.ReevaluateEffectiveValue(AvaloniaProperty property, EffectiveValue current, IValueEntry changedValueEntry, Boolean ignoreLocalValue)
   at Avalonia.PropertyStore.ValueStore.OnBindingValueChanged(IValueEntry entry, BindingPriority priority)
   at Avalonia.PropertyStore.BindingEntryBase`2.<SetValue>g__Execute|38_0(BindingEntryBase`2 instance, BindingValue`1 value)
   at Avalonia.PropertyStore.BindingEntryBase`2.SetValue(BindingValue`1 value)
   at Avalonia.PropertyStore.BindingEntryBase`2.OnNext(TSource value)
   at Avalonia.Data.TemplateBinding.PublishValue()
   at Avalonia.AvaloniaObject.RaisePropertyChanged[T](AvaloniaProperty`1 property, Optional`1 oldValue, BindingValue`1 newValue, BindingPriority priority, Boolean isEffectiveValue)
   at Avalonia.AvaloniaObject.SetAndRaise[T](DirectPropertyBase`1 property, T& field, T value)
   at Avalonia.Controls.TabControl.set_SelectedContent(Object value)
   at Avalonia.Controls.TabControl.<UpdateSelectedContent>b__45_0(Object v)
   at Avalonia.Reactive.LightweightObservableBase`1.Subscribe(IObserver`1 observer)
   at Avalonia.Controls.TabControl.UpdateSelectedContent(Control container)
   at Avalonia.Controls.TabControl.<>c.<.cctor>b__10_0(TabControl x, AvaloniaPropertyChangedEventArgs e)
   at Avalonia.Reactive.LightweightObservableBase`1.PublishNext(T value)
   at Avalonia.AvaloniaObject.RaisePropertyChanged[T](AvaloniaProperty`1 property, Optional`1 oldValue, BindingValue`1 newValue, BindingPriority priority, Boolean isEffectiveValue)
   at Avalonia.AvaloniaObject.RaisePropertyChanged[T](DirectPropertyBase`1 property, T oldValue, T newValue)
   at Avalonia.Controls.Primitives.SelectingItemsControl.OnSelectionModelPropertyChanged(Object sender, PropertyChangedEventArgs e)
   at Avalonia.Controls.Selection.SelectionModel`1.RaisePropertyChanged(String propertyName)
   at Avalonia.Controls.Selection.SelectionModel`1.CommitOperation(Operation operation, Boolean raisePropertyChanged)
   at Avalonia.Controls.Selection.SelectionModel`1.EndBatchUpdate()
   at Avalonia.Controls.Selection.SelectionModelExtensions.BatchUpdateOperation.Dispose()
   at Avalonia.Controls.Primitives.SelectingItemsControl.UpdateSelection(Int32 index, Boolean select, Boolean rangeModifier, Boolean toggleModifier, Boolean rightButton, Boolean fromFocus)
   at Avalonia.Controls.Primitives.SelectingItemsControl.UpdateSelection(Control container, Boolean select, Boolean rangeModifier, Boolean toggleModifier, Boolean rightButton, Boolean fromFocus)
   at Avalonia.Controls.Primitives.SelectingItemsControl.UpdateSelectionFromEventSource(Object eventSource, Boolean select, Boolean rangeModifier, Boolean toggleModifier, Boolean rightButton, Boolean fromFocus)
   at Avalonia.Controls.TabControl.OnPointerPressed(PointerPressedEventArgs e)
   at Avalonia.Input.InputElement.<>c.<.cctor>b__32_8(InputElement x, PointerPressedEventArgs e)
   at Avalonia.Reactive.LightweightObservableBase`1.PublishNext(T value)
   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.MouseDevice.MouseDown(IMouseDevice device, UInt64 timestamp, IInputElement root, Point p, PointerPointProperties properties, KeyModifiers inputModifiers, IInputElement hitTest)
   at Avalonia.Input.MouseDevice.ProcessRawEvent(RawPointerEventArgs e)
   at Avalonia.Input.InputManager.ProcessInput(RawInputEventArgs e)
   at Avalonia.Controls.TopLevel.HandleInput(RawInputEventArgs e)
   at Avalonia.Native.WindowBaseImpl.RawMouseEvent(AvnRawMouseEventType type, UInt64 timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
   at Avalonia.Native.Interop.Impl.__MicroComIAvnWindowBaseEventsVTable.RawMouseEvent(Void* this, AvnRawMouseEventType type, UInt64 timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
--- End of stack trace from previous location ---
   at Avalonia.Native.DispatcherImpl.RunLoop(CancellationToken token)
   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, ShutdownMode shutdownMode)
   at AvaloniaApplication1.Program.Main(String[] args) in /Users/gagik/Downloads/RoutedViewHostInsideTabControl/RoutedViewHostInsideTabControl/Program.cs:line 13

Step to reproduce

  1. Download the attached sample
  2. Run the application
  3. Change the tab to Employees
  4. Change the tab back to Companies
  5. Observe an app crash

Reproduction repository

https://github.com/kyurkchyan/RoutedViewHostInsideTabControl

Expected behavior

The tab switching should work seamlessly

Screenshots 🖼️

No response

IDE

Rider macOS

Operating system

Mac OS 14.0 (23A344)

Version

Avalonia.ReactiveUI 11.0.5

Device

Mac

ReactiveUI Version

19.5.1

Additional information ℹ️

No response

@kyurkchyan kyurkchyan added the bug label Nov 24, 2023
@kyurkchyan
Copy link
Author

We've resolved this issue by copying the RoutedViewHost source code and adding the following inside the WhenActivated block

this.WhenActivated(disposables =>
            {
                ....

                Disposable.Create(() =>
                          {
                              Content = null;
                          })
                          .DisposeWith(disposables);
            });

This resolves the root cause of the issue. The problem is that once the view is reactivated, the same content is recreated through view location. However, that view is already attached to the parent. This means whenever the routed view host deactivates, we also need to clear its content. Otherwise, we are risking double-attaching the same view and causing this exception.

@glennawatson
Copy link
Contributor

Probably worth raising this up with the Avalonia team. See if you can get the fix in.

@kyurkchyan
Copy link
Author

@glennawatson you are actually right. I've extended the sample I've attached to this bug report. And instead of using RoutedViewHost when using bare TransitioningContentControl through assigning the Content property the same issue occurs. I will raise this Avalonia.

@kyurkchyan
Copy link
Author

Closing this issue for the time being, for 2 reasons

  1. There's a workaround
  2. Most probably this should be fixed inside the TransitioningContentControl and not the RoutedViewHost. For that, an issue is opened inside the Avalonia repo

@glennawatson
Copy link
Contributor

Worth noting that AvaloniaUI own both the RoutedViewHost and the TransitioningContentControl

https://github.com/AvaloniaUI/Avalonia/blob/master/src/Avalonia.ReactiveUI/RoutedViewHost.cs

So bugs with that system aren't necessarily related directly with ReactiveUI.

@kyurkchyan
Copy link
Author

@glennawatson thanks for the info, I actually didn't realize that. And the good news is the issue is already fixed in the latest CI beta release.

Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Dec 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants