Object disposing in Xamarin.Forms

前端 未结 3 1149
庸人自扰
庸人自扰 2021-01-17 12:56

I\'m looking for the right way to dispose objects in a Xamarin Forms application. Currently i\'m using XAML and MVVM coding style. Then from my view model i get a reference

相关标签:
3条回答
  • 2021-01-17 13:31

    We were getting disposed of object exceptions in Forms when Bindings to ListViews or Labels changed values as pages/fragments were being disposed of. I'm assuming you could dispose of objects in your ViewModel the same place we were removing bindings.

    protected override void OnParentSet()
    {
        base.OnParentSet();
    
        if (Parent == null)
        {
            //Clear a bunch of bindings or dispose of ViewModel objects 
            BindingContext =
                _listView.ItemsSource = null;
        }
    }
    
    0 讨论(0)
  • 2021-01-17 13:33

    I had pretty much the same requirement a couple of weeks ago. I wanted to make sure that event subscriptions in my view models would be unsubscribed when the page is closed. After a lot of research my conclusion was that the simplest solution was to use the ContentPage.OnDisappearing method.

    As you pointed out the object you want to dispose is in your ViewModel, so you need a little bit of infrastructure to make sure your ViewModel is informed when the it's disappearing. To do that I defined a base implementation of my view model that had two key methods OnAppearing and OnDisappearing (note this was a class rather than an interface because I have other base functionality such as IPropertyNotify implementation - not shown here).

    public class ViewModelBase
    {
        /// <summary>
        /// Called when page is appearing.
        /// </summary>
        public virtual void OnAppearing()
        {
            // No default implementation. 
        }
    
        /// <summary>
        /// Called when the view model is disappearing. View Model clean-up should be performed here.
        /// </summary>
        public virtual void OnDisappearing()
        {
            // No default implementation. 
        }
    }
    

    Then I subsclassed ContentPage and override the OnAppearing and OnDisappearing methods and then use them to notify my view model.

    public class PageBase : ContentPage
    {
        /// <summary>
        /// Performs page clean-up.
        /// </summary>
        protected override void OnDisappearing()
        {
            base.OnDisappearing();
    
            var viewModel = BindingContext as ViewModelBase;
    
            // Inform the view model that it is disappearing so that it can remove event handlers
            // and perform any other clean-up required..
            viewModel?.OnDisappearing();
        }
    
        protected override void OnAppearing()
        {
            base.OnAppearing();
    
            // Inform the view model that it is appearing
            var viewModel = BindingContext as ViewModelBase;
    
            // Inform the view model that it is appearing.
            viewModel?.OnAppearing();
        }
    }
    

    Then when you implement a page just make sure that it is of type PageBase:

    <?xml version="1.0" encoding="utf-8" ?>
    <pages:PageBase xmlns="http://xamarin.com/schemas/2014/forms"
              xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
              xmlns:controls="clr-namespace:Forms.App.Controls;assembly=Forms.App"
              xmlns:converters="clr-namespace:Forms.App.Converters;assembly=Forms.App"
              xmlns:pages="clr-namespace:Forms.App.Pages;assembly=Forms.App"
              x:Class="Forms.App.Pages.LogonPage"
              NavigationPage.HasNavigationBar="False"
              Title="Logon">
    

    And in your ViewModel you can then override your OnDisappearing method and dispose your objects:

    public class FormViewModel : ViewModelBase
    {
        public override void OnDisappearing()
        {
            base.OnDisappearing();
    
            // Dispose whatever objects are neede here
        }
    }
    

    Just one thing to watch out for - if you're using stack navigation the OnDisappearing method gets called when you stack another page on-top of your current page (your page is disappearing temporarily after all). So you will need to cater for this and probably not dispose your object in that case. However if you're not stacking anything on-top of your page there is nothing to worry about. In my case it was just event subscriptions so I attached the event handlers in the OnAppearing and detached them on the OnDisappearing.

    I hope that helps you out!

    0 讨论(0)
  • 2021-01-17 13:53

    I have View Models that conform to IDisposable. So I needed a way for the Page's BindingContext to be disposed when the page is no longer needed.

    I used the suggestion of Nick which uses OnParentSet being set to NULL to known when the page is no longer needed.

    The SafeContentPage class can be used in place of ContentPage. Iff The binding context supports IDisposable will it automatically try to dispose the binding context.

    public class SafeContentPage : ContentPage
    {
        protected override void OnParentSet()
        {
            base.OnParentSet();
            if (Parent == null)
                DisposeBindingContext();
        }
    
        protected void DisposeBindingContext()
        {
            if (BindingContext is IDisposable disposableBindingContext) {
                disposableBindingContext.Dispose();
                BindingContext = null;
            }
        }
    
        ~SafeContentPage()
        {
            DisposeBindingContext();
        }
    }
    

    The OnDisappearing method isn't a reliable technique as there are platform differences in terms of when it is called, and just because the page disappeared doesn't mean its View Model is no longer needed.

    0 讨论(0)
提交回复
热议问题