Skip to content

Conversation

@HrlecMatej
Copy link
Contributor

@HrlecMatej HrlecMatej commented Jul 9, 2020

fix: windows-common ViewModelViewHost creating the old view again when switching to a new view model (#2258)

What kind of change does this PR introduce?

Bug fix

What is the current behavior?

fixes #2258
windows-common ViewModelViewHost creates the old view again when switching to a new view model:
The way it was written before, a change of either ViewModelProperty or ViewContractObservableProperty resulted in both firing their corresponding Observables. Because of CombineLatest this resulted in the old ViewModel being run through ResolveViewForViewModel again, when the ViewModelProperty was changed. This created the old View for a moment again, which is a waste of resources, and it results in animation flickering (and other issues with DI lifetime management which I use).

What is the new behavior?

Now each Dependency property only fires their own Observable, instead of both at the same time.
The implementation has been mostly copied from XamForms ViewModelViewHost

What might this PR break?
It's a simple change, that has been taken from another part of ReactiveUI, so I find it unlikely to break anything.

Please check if the PR fulfills these requirements

  • Tests for the changes have been added (for bug fixes / features)
    I cannot run the ReactiveUI.Tests, because I am missing build tools for all the multitude of test targets. I do not think this class even has any tests anyhow.
  • Docs have been added / updated (for bug fixes / features)
    I would not know, where to look for that. Besides, this bug fix makes ViewModelViewHost ViewModelProperty follow expected behavior, so no documentation changes should be necessary.

Other information:
I suggest ReactiveUI's team to do an audit of the 3 ViewModelViewHost implementations, with the goal of putting shared code in a common class. Currently, there is a lot of repetition and a showcase, how to write the same thing in 3 different ways (ViewLocator part of the code being the biggest offender).

…n switching to a new view model (#2258)

The way it was written before, a change of either ViewModelProperty or ViewContractObservableProperty resulted in both firing their corresponding Observables. Because of CombineLatest this resulted in the old ViewModel being run through ResolveViewForViewModel again, when the ViewModelProperty was changed.
@HrlecMatej HrlecMatej requested review from a team July 9, 2020 10:22
var contractChanged = _updateViewModel.Select(_ => ViewContractObservable).Switch();
var viewModelChanged = _updateViewModel.Select(_ => ViewModel);
// Observable that fires when ViewModel or ViewContractObservable change, or ViewContractObservable fires
var vmAndContract = Observable.CombineLatest(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to cause memory leaks unfortunately due to the way dependency properties work. Eg WhenAnyValue will call into the static manager, and it'll hold an instance of the class until you dispose the WhenAnyValue.

This is the reason why the SomethingChanged exists since it doesn't suffer from this problem.

Happy to take a solution that keeps the SomethingChanged approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not know that could be an issue. Is that not a problem in https://github.com/reactiveui/ReactiveUI/blob/main/src/ReactiveUI.XamForms/ViewModelViewHost.cs as well then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Xamarin Forms is different since they have BindableProperty invoke a INotifyPropertyChanged so have a non-memory leaking way of getting alerted of property changes.

Copy link
Contributor

@glennawatson glennawatson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will just need the requested changes done.

@glennawatson
Copy link
Contributor

Also in note regards to integrating the Xamarin Forms/Windows version, we can't do the xamarin forms version on windows due to the fact Xamarin Forms will use INotifyPropertyChanged approach for alerting you when BindableProperty's change.

…gain when switching to a new view model (#2258)"

This reverts commit 7dd2f58.
…n switching to a new view model (#2258)

Reverted my first pull request, and added a new subject for ViewContract to handle that separately
@glennawatson
Copy link
Contributor

Thanks for the quick update. Looks heaps better thanks.

@HrlecMatej
Copy link
Contributor Author

So, I reverted my first commit, and simply added a new Subject for ViewContract changes and separate PropertyChangedCallbacks.
This will still fix the issue of triggering both properties at the same time, while preventing the memory leak you mentioned.

@glennawatson
Copy link
Contributor

The build error is due to a recent change in Splat, so will fix it. Thanks.

The ViewContractObservable was previously set before the CombineLatest operator. That also means value change callback on ViewContractObservableProperty fires before CombineLatest. This meant, if we never set ViewContract afterwards, CombineLatest would never emit a value, since _updateViewContract would notify of a change before anyone was listening.
This commit fixes that by simply moving the ViewContractObservable setup to the end.
Alternatively, we could also make _updateViewContract into a ReplaySubject(1).
@HrlecMatej
Copy link
Contributor Author

HrlecMatej commented Jul 10, 2020

After doing some tests with my project, I found a subtle bug changing ViewContract change notification caused.
The ViewContractObservable was previously set before the CombineLatest operator. That causes value change callback on ViewContractObservableProperty to fire before CombineLatest. This meant, if we never set ViewContract afterwards, CombineLatest would never emit a value, since _updateViewContract would notify of a change before anyone was listening.
This commit fixes that by simply moving the ViewContractObservable setup to the end.
Alternatively, we could also make _updateViewContract into a ReplaySubject(1) or BehaviorSubject.

@glennawatson glennawatson reopened this Jul 10, 2020
@glennawatson glennawatson merged commit ea8f2c0 into reactiveui:main Jul 10, 2020
@glennawatson
Copy link
Contributor

I'll do a release by the end of the week coming. Thanks for getting this in and testing it for me.

Just want to get some events bugs fixed first before the release.

You can use the GitHub package nuget server if you need it sooner though.

@HrlecMatej
Copy link
Contributor Author

You're welcome. Also, thank you for being so responsive.

@github-actions
Copy link

This pull request 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 Nov 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] WPF - ViewModelViewHost creates the old view again when switching to a new view model

3 participants