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

RoutedViewHost remains subscribed to the current view model of previously connected Routers. #17058

Open
IanRawley opened this issue Sep 18, 2024 · 0 comments
Labels

Comments

@IanRawley
Copy link
Contributor

Describe the bug

In my application, I'm trying to create multiple routing stacks so they can be swapped between while preserving routing history. I've done this by placing multiple RoutingState members in the MainViewModel and then a public Router property (to satisfy IScreen) which points to the currently active one.

This works fine most of the time, however if after swapping RoutingStates one of the previously connected RoutingStates navigates to a new ViewModel, the RoutedViewHost will display the appropriate view for that viewmodel no matter which RoutingState is the currently active one, causing active Router and RoutedViewHost to "desync".

To Reproduce

  1. Clone https://github.com/IanRawley/MultiRouterRoutingExample.git
  2. Run the app, you should see a screen like this:
    multirouterexample
  3. Try clicking buttons. The two at the top toggle the current router without navigating. The two at the bottom toggle the active router and navigate both stacks starting with the currently active one. As a result, if you click "Navigate and Toggle" the Avalonia RoutedViewHost displays the contents of the top of the appropriate active stack. However if you click "Toggle and Navigate" it always desyncs, displaying the contents of the top of the inactive stack because the inactive stack was the last to add a new ViewModel to its internal stack.

Expected behavior

RoutedViewHost always displays the appropriate content for the top of the currently bound Router RoutingState, regardless of what previously bound RoutingState's do with their stacks.

Avalonia version

11.2.0-beta2

OS

No response

Additional context

I tracked down the problem to how the RoutedViewHost manages its subscriptions. See the following code from here:

            this.WhenActivated(disposables =>
            {
                var routerRemoved = this
                    .WhenAnyValue(x => x.Router)
                    .Where(router => router == null)!
                    .Cast<object?>();

                var viewContract = this.WhenAnyValue(x => x.ViewContract);

                this.WhenAnyValue(x => x.Router)
                    .Where(router => router != null)
                    .SelectMany(router => router!.CurrentViewModel)
                    .Merge(routerRemoved)
                    .CombineLatest(viewContract)
                    .Subscribe(tuple => NavigateToViewModel(tuple.First, tuple.Second))
                    .DisposeWith(disposables);
            });

SelectMany() doesn't unsubscribe from previously selected observables, resulting in an observable that produces every CurrentViewModel from all the combined CurrentViewModel observables of any RoutingState that has ever been assigned to the Router property.

I copied the entirety of RoutedViewHost into a test class (TweakedRoutedViewHost) and replaced the SelectMany() call with a Select().Switch() chain. Switch unsubscribes from previous observables, so old RoutingStates should no longer be pushing ViewModels to the RoutedViewHost. This seems to fix the desync, but I'm not sure of any side effects it may have.

@IanRawley IanRawley added the bug label Sep 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant