dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.27k stars 1.76k forks source link

Call Dispose() on Page and ViewModel when the page is popped if they implement IDisposable #7354

Closed ederbond closed 7 months ago

ederbond commented 2 years ago

Description

It has been 2 years since I've opened this bug, and MSFT didn't come up with a proper solution, so to save your precious time consider completely ditching MAUI Shell for now and use the following library (based on PRISM v8) to workaround this issue:

https://github.com/naveasy/Naveasy

It would be amazing if MAUI framework could call the Dispose method on the Page and also on it's ViewModel when the page gets removed from the navigation stack so we developers can gracefully dispose object that we have used and will no longer be used. It would be even better if the same could be done on custom controls (Views) that we create.

Steps to Reproduce

Implement IDisposable on a given page; Also Implement IDisposable on it's page's VM

Current behavior The Dispose() method is never called after we remove the page from the navigation stack.

Expected behavior: Dispose() to be called when the page is removed from the navigation stack;

Version with bug

Release Candidate 2 on .NET 8

Last version that worked well

Unknown/Other

Affected platforms

iOS, Android, Windows, macOS, Other (Tizen, Linux, etc. not supported by Microsoft directly)

Affected platform versions

Android 11 and iOS, Windows, Tizen, MacOS ...

Did you find any workaround?

No

Relevant log output

No response

Auto72 commented 10 months ago

I already use FlyoutBehavior.Disabled, thanks. What I need is to destroy the pages manually. I tryed the following code, but I can't find .Content or .ContentTemplate in the item object. 🤔

if (Application.Current?.MainPage is not Shell shell) { return; }
foreach(var item in shell.Items)
{
    item.
}
albyrock87 commented 10 months ago

@Auto72 this is what I meant with shell.Items[x][y][z]

foreach (var item in shell.Items)
        {
            foreach (var section in item.Items)
            {
                foreach (var content in section.Items)
                {
                    content.Content = null;
                    content.ContentTemplate = null;
Auto72 commented 10 months ago

The loop above works, thanks. I found content.Content is always null for my pages. Anyway, after that, if I try to navigate to the LoginPage, I get the following error:

await Current.GoToAsync("///login");

"No Content found for ShellContent, Title:My App Login, Route login"

Instaed of null, should I put new LoginPage();

albyrock87 commented 10 months ago

@Auto72 after clearing you have to set new ones obviously.

Anyway, let's not make this thread a support page :) Especially because you're trying to do something not provided and not documented by Microsoft - one of the reason I created Nalu.Maui package.

MichaelLHerman commented 9 months ago

I'm calling Dispose by overriding OnParentSet in a base class for all my pages since popping is setting parent to null causing bindings to be reevaluated and I need Dispose to be called before that happens

        protected override void OnParentSet()
        {
            if (Parent == null)
            {
                Dispose();
                if (BindingContext is IDisposable bindingContextDisposable)
                {
                    bindingContextDisposable.Dispose();
                }
            }
            base.OnParentSet();
        }
System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.TryGetValue(Object source, Object& value)
Microsoft.Maui.Controls.BindingExpression.ApplyCore(Object sourceObject, BindableObject target, BindableProperty property, Boolean fromTarget, SetterSpecificity specificity)
Microsoft.Maui.Controls.BindingExpression.Apply(Boolean fromTarget)
Microsoft.Maui.Controls.Binding.Apply(Boolean fromTarget)
Microsoft.Maui.Controls.BindableObjectExtensions.RefreshPropertyValue(BindableObject self, BindableProperty property, Object value)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Internals.PropertyPropagationExtensions.PropagatePropertyChanged(String propertyName, Element element, IEnumerable children)
Microsoft.Maui.Controls.VisualElement.Microsoft.Maui.Controls.IPropertyPropagationController.PropagatePropertyChanged(String propertyName)
Microsoft.Maui.Controls.Element.OnParentSet()
Microsoft.Maui.Controls.NavigableElement.OnParentSet()
Microsoft.Maui.Controls.Page.OnParentSet()
Microsoft.Maui.Controls.Element.SetParent(Element value)
Microsoft.Maui.Controls.Element.OnChildRemoved(Element child, Int32 oldLogicalIndex)
Microsoft.Maui.Controls.Element.RemoveLogicalChild(Element element, Int32 index)
Microsoft.Maui.Controls.Element.RemoveLogicalChild(Element element)
Microsoft.Maui.Controls.Platform.ModalNavigationManager.PopModalAsync(Boolean animated)
Microsoft.Maui.Controls.Shell.NavigationImpl.OnPopModal(Boolean animated)
Microsoft.Maui.Controls.ShellSection.PrepareCurrentStackForBeingReplaced(ShellNavigationRequest request, ShellRouteParameters queryData, IServiceProvider services, Nullable`1 animate, List`1 globalRoutes, Boolean isRelativePopping)
Microsoft.Maui.Controls.ShellSection.GoToAsync(ShellNavigationRequest request, ShellRouteParameters queryData, IServiceProvider services, Nullable`1 animate, Boolean isRelativePopping)
Microsoft.Maui.Controls.ShellNavigationManager.GoToAsync(ShellNavigationParameters shellNavigationParameters, ShellNavigationRequest navigationRequest)
Microsoft.Maui.Controls.Shell.NavigationImpl.OnPopModal(Boolean animated)
CadabraMobile.Services.Common.NavigationService.PopModal(Boolean animate)
memis1970 commented 9 months ago

https://github.com/dotnet/maui/issues/7329

Approaching 2 years ago @jfversluis mentioned providing more clarity on this issue was being worked on. Can someone direct me to this?

Thanks

albyrock87 commented 9 months ago

@memis1970 after a lot of research I can tell you ShellContents work like this by design, that's because while using Tabs or TabBar, the content on each tab needs to stay alive. Ok top of that each ShellSection keeps its own navigation stack, also by design.

memis1970 commented 9 months ago

@albyrock87 Thanks for the comment. This is true for the Flyout also. It's a major headache for me coming from Prism and being unable to get a Flyout Page working without Shell the way I need.

I've found this library https://github.com/BurkusCat/Burkus.Mvvm.Maui which has managed it but it's an absurd amount of hoops to jump through to implement the navigation.

odahan commented 7 months ago

Is there any progress, or at least any hope? Reminder: we just need, at the very least, the Transient ViewModels to act as a Transient, nothing more, nothing less. That's not the only problem, but it's a big one. When I register a Transient service or VM, I don't want it to act as a Singleton, that means I want a new instance each time. Definition of Transient. It's as simple as that. And I don't want to just have convoluted explanations telling me why it doesn't work the way it should but that it's "normal". So, any news after so many years, or is MAUI dead?

taublast commented 7 months ago

A very raw "maybe":

  1. add new properties to any Element like

    • bool AutoDisposeChildren
    • bool AutoDisposeBindingContext
  2. when Handler goes null trigger the

    • disposing of the binding context (bye bye ViewModel) if AutoDisposeBindingContext
      
      (if BindingContext is IDisposable disposable)
      {
      disposable.Dispose();
      }

* dispose children `if AutoDisposeChildren`
could either iterate through InternalChildren or Children for ILayour or other options

Possible problems:
* Handler could go null several times (`Shell` loved to kill/reinstate handler for same VirtualView several times i guess) (?)
* Disposing already disposed objects (?)

Might resolve possible problems by using custom interface IMauiDisposable : IDisposable, to have `IsDisposed` etc..
odahan commented 7 months ago

I found a sort of solution by just adding the following code into AppShell.cs :

ShellContent _previousShellContent;
    protected override void OnNavigated(ShellNavigatedEventArgs args)
    {
        base.OnNavigated(args);
        if (CurrentItem?.CurrentItem?.CurrentItem is not null &&
            _previousShellContent is not null)
        {
            var property = typeof(ShellContent)
                .GetProperty("ContentCache", BindingFlags.Public |
                                 BindingFlags.NonPublic | BindingFlags.Instance |
                                 BindingFlags.FlattenHierarchy);
            property.SetValue(_previousShellContent, null);
        }
        _previousShellContent = CurrentItem?.CurrentItem?.CurrentItem;
    }

Far from being perfect, I think there's a problem with tabs (but I don't use them in this application), and the only way to solve this problem will be to get an official fix from Microsoft. But when ?

albyrock87 commented 7 months ago

@odahan that way of clearing the shell content is the same I'm using in Nalu.Navigation and as you mentioned it doesn't work while switching between tabs, and that's by design: from an UX point of view it is incorrect to destroy the state of a tab because the user usually expect that to persist while switching between tabs. Not to mention the bottom navigation bar.

The topic is really complex, that's why I created that abstraction on top of Shell: to simplify the usage while keeping the good behaviors provided by Shell.

At this point we shouldn't expect something from Microsoft, simply because people are using Shell the way it is, so any change would become a breaking one. Also remember that Shell is something ported from Xamarin, which is another reason for them to avoid changes.

PureWeen commented 7 months ago

@odahan that way of clearing the shell content is the same I'm using in Nalu.Navigation and as you mentioned it doesn't work while switching between tabs, and that's by design: from an UX point of view it is incorrect to destroy the state of a tab because the user usually expect that to persist while switching between tabs.

Not to mention the bottom navigation bar.

The topic is really complex, that's why I created that abstraction on top of Shell: to simplify the usage while keeping the good behaviors provided by Shell.

At this point we shouldn't expect something from Microsoft, simply because people are using Shell the way it is, so any change would become a breaking one. Also remember that Shell is something ported from Xamarin, which is another reason for them to avoid changes.

If folks want to use the IDisposable pattern I'd highly recommend using Nalu.Navigation. Implementing IDiposable, and not creating your own service scopes, will most likely cause memory leaks https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-guidelines#disposable-transient-services-captured-by-container. Even if you manually call dispose, afaik, that won't really accomplish anything, other then your code getting called. The instance will still be rooted in the container by design of how the Microsoft containers work. If you want to roll your own thing, and you aren't going to use service scopes, then don't implement IDisposable, just make something up like IDestroy.

The tabbed page conversation is a great example of why we can't make the decisions of all cases where we should be calling 'destroy'. It comes down to providing enough information and hooks to establish the rules for your app.

Most fixes here that I'd envision are based around adding better hooks for IServiceProvider to setup your own IServiceProvider scopes and make the navigation args less useless. If navigation args had better information about push/pop/and pages involved you could make some of your own decisions.

We're primarily not focusing on this issue because we're heavily focused on quality and Xamarin migration. Nothing changed here between Xamarin and MAUI, so it's just not a priority right now. That being said, I think there's some easy net9 wins here.

Some areas folks can create PRs for. I'm hoping we get time to look into these things but it's hard to know.

I would also be really curious to know any APIs or behavior improvements we could add to make @albyrock87's life easier. @albyrock87, if you're open to chat more actively message me on twitter or discord https://aka.ms/dotnet-discord

dansiegel commented 7 months ago

Honestly your easiest bet is to use Prism where you have helpers like IActiveAware, IPageLifeCycleAware, INavigationAware, and IDestructible. But if you really want to implement something to help you here I would actually advise against using IDisposable because .NET itself calls it in weird times/places which is why Prism created a unique interface to deal with disposing resources.

If you want to go the difficult route and not use Prism you could create a behavior that invokes your cleanup method when the behavior is detaching from the View.

public class DisposableBehavior : Behavior
{
    protected override void OnDetachingFrom(BindableObject bindable)
    {
        base.OnDetachingFrom(bindable);
        if (bindable.BindingContext is IDisposable disposable)
            disposable.Dispose();
    }
}
<ContentPage xmlns:behaviors="using:MyApp.Behaviors">
  <ContentPage.Behaviors>
    <behaviors:DisposableBehavior />
  </ContentPage.Behaviors>
</ContentPage>
odahan commented 7 months ago

Indeed Alberto, as I mentioned it, the only way out is to get some fix from Microsoft... Until this day we all have different strategies that work only in a given app. Is MS working on this since a so long time ? Nobody knows. One day my prince will come... :-)

Le dim. 7 avr. 2024 à 17:59, Alberto Aldegheri @.***> a écrit :

@odahan https://github.com/odahan that way of clearing the shell content is the same I'm using in Nalu.Navigation https://github.com/nalu-development/nalu and as you mentioned it doesn't work while switching between tabs, and that's by design: from an UX point of view it is incorrect to destroy the state of a tab because the user usually expect that to persist while switching between tabs. Not to mention the bottom navigation bar.

The topic is really complex, that's why I created that abstraction on top of Shell: to simplify the usage while keeping the good behaviors provided by Shell.

At this point we shouldn't expect something from Microsoft, simply because people are using Shell the way it is, so any change would become a breaking one. Also remember that Shell is something ported from Xamarin, which is another reason for them to avoid changes.

— Reply to this email directly, view it on GitHub https://github.com/dotnet/maui/issues/7354#issuecomment-2041514193, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGIXCAPJ2PDMLDWKDVBR6DY4FUOLAVCNFSM5WN3AS62U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBUGE2TCNBRHEZQ . You are receiving this because you were mentioned.Message ID: @.***>

-- Olivier Dahan

taublast commented 7 months ago

It becomes very clear to me now that a MAUI-specific interface should be implemented for that purpose, in a way they did with IQueryAttributable etc, then if people wish they can use it without breaking anything existing. This might be done deep in Core on a VisualElement level and Shell etc should just adapt to it, people should not be forced to use Shell and similar to profit from basic features.

It's okay this is still in discussion, no magic MS prince/princess would come until he/she reads all the opinions. :)

odahan commented 7 months ago

Haha, thanks! Yes, before Prince MS comes to solve the problem, he should come and listen to us :-)

Le lun. 8 avr. 2024 à 11:49, Nick Kovalsky @.***> a écrit :

It becomes very clear to me now that a MAUI-specific interface should be implemented for that purpose, in a way they did with IQueryAttributable etc, then if people wish they can use it without breaking anything existing. This might be done deep in Core on a VisualElement level and Shell etc should just adapt to it, people should not be forced to use Shell and similar to profit from basic features.

It's okay this is still in discussion, no magic MS prince/princess would come until he/she reads all the opinions. :)

— Reply to this email directly, view it on GitHub https://github.com/dotnet/maui/issues/7354#issuecomment-2042322016, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGIXCG4J2LR44VSZ33EUMTY4JR2FAVCNFSM5WN3AS62U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBUGIZTEMRQGE3A . You are receiving this because you were mentioned.Message ID: @.***>

-- Olivier Dahan

janseris commented 7 months ago

Wait so how is this done in other XAML UI frameworks with DI? Is it a general issue of the DI container?

odahan commented 7 months ago

I have to admit that I've never had to ask myself this question, neither in WPF nor in Silverlight, nor in any version of C#/XAML, and whatever MVVM toolkit I use... The problem arises with MAUI and certainly only with the Shell (which was very new in Xamarin.Forms and which no project I worked on carried out at the time was yet using). So it's very specific, it's not the fault of .NET Core, it's probably not the fault of MAUI as such, it's just a problem of poorly conceived Shell implementation...)

Le lun. 8 avr. 2024 à 12:06, janseris @.***> a écrit :

Wait so how is this done in other XAML UI frameworks with DI?

— Reply to this email directly, view it on GitHub https://github.com/dotnet/maui/issues/7354#issuecomment-2042355678, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABGIXCGHNZIJZ3OZHI2YV6DY4JT3BAVCNFSM5WN3AS62U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TEMBUGIZTKNJWG44A . You are receiving this because you were mentioned.Message ID: @.***>

-- Olivier Dahan

chrisg32 commented 7 months ago

+1 for an interface. This is the lowest hanging fruit: A reliable way knowing when a view is removed and then we can handle the rest e.g. IViewLifecycleAware. If we could get an this interface, I think that would solve all our problems.

One step further with be a way to decorate the shell with a service that manages the lifecycle of the dependency objects e.g. IViewLifecycleManager. The default lifecycle manager could be the current behavior so as not to introduce breaking changes and then provide a second lifecycle manager that calls Dispose on the view model when the pages are removed.

This week I am starting the port of our application to Maui. In our application, memory management is critical. We have been bitten by third party navigation stacks in the past and for this iteration we were hoping to keep navigation and DI as simply as possible. That is looking less and less like a possibility.

ederbond commented 7 months ago

This is currently the 2nd most commented issue here on their GitHub, which has been opened like 2 years ago, and MSFT does nothing about it... It's unbelievable! This is what makes us developers to loose faith in MAUI. How can they ignore their developer community for so long? Last year they marketed Shell as the new way to go by setting it as default on the new project template of VS even knowing it suffers from this bad design architecture that doesn't allow us to easily create a memory/reactive efficient app, and then keep ignoring us like if it wasn`t a big deal. This is what happens when you just create "Hello World" apps to showcase on conference shows instead of dogfooding your own stuff.

taublast commented 7 months ago

A very interesting material to consider when thinking about a core-level solution: https://github.com/dotnet/maui/issues/18366#issuecomment-1901160604

dzebas commented 7 months ago

Any chance this is going to be ever looked at? It's such a fundamental issue, any serious app can't be made without it. We started moving our huge enterprise app from Xamarin.Forms to Maui just because Microsoft is forcing everyone, but issues like this makes us very nervous.

ederbond commented 7 months ago

Ask @davidortinau, he was supposed to know it.

janseris commented 7 months ago

Looking at what @davidortinau is doing these days, I found his project where there is an interesting call:

https://github.com/davidortinau/SentenceStudio/blob/main/src/SentenceStudio/MauiProgram.cs

builder.Services.AddTransientWithShellRoute<TPage, TViewModel>() Maybe that could be of your interest. Don't have time to test it.

Another thing that you can notice is that the project uses Service Locator antipattern. I wonder why. Example (https://github.com/davidortinau/SentenceStudio/blob/main/src/SentenceStudio/Pages/Vocabulary/AddVocabularyPageModel.cs):

public AddVocabularyPageModel(IFilePickerService picker, IServiceProvider service)
{
    this.picker = picker;
    _vocabService = service.GetRequiredService<VocabularyService>();
}
ederbond commented 7 months ago

builder.Services.AddTransientWithShellRoute<TPage, TViewModel>()

Doesn't help at all.

pme442 commented 7 months ago

I keep seeing big issues like this getting moved to the Backlog and it makes me nervous, too. I'm 3 months into converting a rather complex app from Xamarin Forms to MAUI and I am not even close to having it production-ready. I've lost a lot of features and functionality that I had in the Xamarin Forms app, and I am spending way too much time figuring out and implementing workarounds. Now I'm trying to get a handle on this memory management problem because it is definitely going to be a show stopper.

dzebas commented 7 months ago

Tried suggestion by @dansiegel just out of interest, but OnDetachingFrom never gets triggered?

I also tried using Unloaded event, but on iOS it calls it even when you push another page top (actually calls event twice!) and also when navigating back, so no idea when going forward or backwards.

I also thought I could use OnNavigateFrom event, but for some dumb reason NavigatedFromEventArgs does not dispose "DestinationPage" property, it's marked as Internal :(

memis1970 commented 7 months ago

@eder-em I'd love to know if you've managed to get it working using Flyout, DI and MVVM (Community Toolkit) without Shell. After many frustrating hours trying, I still haven't managed it.

albyrock87 commented 7 months ago

I see you're all asking to use Shell with IDisposable and I fully understand your frustration/concerns, so I wonder if you've read this message by PureWeen (Microsoft) where the situation is well explained.

For now - as suggested - if you don't want to spend countless hours (trust me) to cook a solution yourself, you can try the library I've built to solve this situation and many other things: it also has an embedded leak detector while debugger is attached. It is still based on Shell but it provides Scoped context on every page so that you can dispose what you have to.

If you don't like that for some reasons, I'd like to hear your opinion by opening an issue there.

And obviously you can always try out many of the other fantastic libraries out there which have their own navigation handling strategy (not based on Shell).

bkaankose commented 7 months ago

We're primarily not focusing on this issue because we're heavily focused on quality and Xamarin migration. Nothing changed here between Xamarin and MAUI, so it's just not a priority right now. That being said, I think there's some easy net9 wins here.

@PureWeen

If the team priority is not shifted to critical issues/needs like this case sooner or later there is no point of team to work on Xamarin migration. You can just partner with Google or Facebook to make a new partially working tool to migrate apps from Xamarin to Flutter or React Native. No joke...

What is the point of migrating to MAUI when you can't even guarantee one of the most critical things like page lifecycle? People want literally simple things that work reliable in supported platforms. I don't know what your metrics tell you (also don't care because they are mostly misleading the management) but developers care only about 2 platforms: iOS and Android.

Finito. Prism does this well. I wonder why this new shiny framework cannot manage something that 10+ years library is handling so well. Treat each NavigationPage as Frame and handle the stack gracefully.

I know you guys working hard to on the issues, lack of resources and bad management/architectural decisions already. For once, please stop listening people who has no idea about what they do in Xamarin/MAUI and give an ear to those who are trying to do things systematically in 10 levels deep.

brianlagunas commented 7 months ago

Thanks @eder-em for the recommendation and confidence. @davidortinau you can send the job offer letter to @dansiegel and myself to support@prismlibrary.com. We'll be happy to help improve things 😄

I would like to point out a few clarifications about the statements made about Prism. First, the Patterns and Practices team, which was made up of MS employees, contractors, and community members like myself, were responsible for the initial creation of Prism for WPF. Prism later had a brief attempt at a Windows Store (UWP) version which failed spectacularly along with the UWP framework itself. I then took complete ownership of the project and personally created the XF version from scratch. This is the time Dan came onboard and he is responsible for the MAUI version we have today using the XF version as inspiration. It's been a fun and interesting journey.

It is true that Prism does now have a paid commercial license. However, it is still free for any company/person that meets the community license requirements. We understand this doesn't help everyone, but Prism does solve a lot of the problems that are being mentioned in this thread, which is a strong motivation for companies purchasing a commercial license.

As to this issue specifically, Prism was not able to use the existing IDisposible interface due to the complexities around the intricacies of .NET and garbage collection. Which is why we had to introduce a new interface called IDestructible. Maybe the MAUI team can take a similar approach.

Better yet, Dan and I can sell Prism to Microsoft and then just make it the default behavior. What do you say MS? Ready for another acquisition? LOL

dzebas commented 7 months ago

I came up with this solution/hack for now until Maui team can implement something better internally. Seems to be working fine for our project.

Not using IDispose, but created our own IDestructible interface. Basically in our base page when OnNavigatedFrom is called I check if page is still present in either navigation or modal stacks, if yes do nothing as it means moving forward, otherwise call Destroy if page/viewmodel implements interface.

protected override void OnNavigatedFrom( NavigatedFromEventArgs args )
{
    base.OnNavigatedFrom( args );

    // is page still present
    if (Shell.Current.Navigation.NavigationStack.Any( p => p == this ) || 
        Shell.Current.Navigation.ModalStack.Any( p => p == this )) 
        return;

    var page = this as IDestructible;
    var vm = _viewModel as IDestructible;

    // nothing to destroy
    if (page is null && vm is null)
        return;

    vm?.Destroy();
    BindingContext = null;
    page?.Destroy();
}
ederbond commented 7 months ago

Hi @memis1970 I'm @eder-em (I have 2 accounts here). Here is the framework that I have created with my friend Leo to workaround the issue.

https://github.com/naveasy/Naveasy

pictos commented 7 months ago

Tried suggestion by @dansiegel just out of interest, but OnDetachingFrom never gets triggered?

I also tried using Unloaded event, but on iOS it calls it even when you push another page top (actually calls event twice!) and also when navigating back, so no idea when going forward or backwards.

I also thought I could use OnNavigateFrom event, but for some dumb reason NavigatedFromEventArgs does not dispose "DestinationPage" property, it's marked as Internal :(

Try with the PlatformBevahior, by default Behaviors doesn't call OnDetaching at framework level (since XF). The new PlatformBehavior does, so it will best to use it instead. The API for xaml will not change.

dzebas commented 7 months ago

Hi @pictos, thank you, PlatformBevahior works, but I found an issue (same in my code above) - OnDetachedFrom is called on current page when you push another page on top and also when changing TabBar tabs. No idea what it is detaching from if it's still in navigation stack.

Looks like you can't win with Maui.

memis1970 commented 7 months ago

An acquisition of PRISM including the current maintainers of PRISM to help lead the project instead of @davidortinau would be my dream 😂.

Seconded! :-)

@ederbond Thanks for pointing out your library! I'll take a look. Appreciate it.

brianlagunas commented 7 months ago

Hi @memis1970 I'm @eder-em (I have 2 accounts here). Here is the framework that I have created with my friend Leo to workaround the issue.

https://github.com/naveasy/Naveasy

If you're going to copy Prism code, you can at least give Prism the credit and not to try to pass it off as your own work. I understand that the official Prism project is no longer MIT, and businesses that make more than $1M a year want an alternative free version because they don't want to support the official framework. However, assuming you at least used the v8 MIT version of the code and didn't purposefully break the new Prism license, it would be great if you could at least acknowledge the code you are using from Prism. Thank you.

davidortinau commented 7 months ago

Thanks all for the good discussion. As written, this proposal is not aligned with where we are currently investing.

To narrow the conversation, @PureWeen has drafted 2 specs to consider for .NET 9.

21814

21816

ederbond commented 7 months ago

Hi @memis1970 I'm @eder-em (I have 2 accounts here). Here is the framework that I have created with my friend Leo to workaround the issue.

https://github.com/naveasy/Naveasy

If you're going to copy Prism code, you can at least give Prism the credit and not to try to pass it off as your own work. I understand that the official Prism project is no longer MIT, and businesses that make more than $1M a year want an alternative free version because they don't want to support the official framework. However, assuming you at least used the v8 MIT version of the code and didn't purposefully break the new Prism license, it would be great if you could at least acknowledge the code you are using from Prism. Thank you.

Hi @brianlagunas I have updated the readme.md of Naveasy to give the proper credits to the PRISM team.