Search code examples
c#xamarinxamarin.formstimeronresume

OnResume Page Content not updating


I have an Android Widget button that when pressed inserts a record into an SQLite database.

I have an issue when the app is used this situation:

  1. The user pauses the app to use the widget on their device home screen.
  2. The user presses the widget button which inserts the new record.
  3. The user resumes the Xamarin.Forms page.

When the page is resumed, OnAppearing is automatically called. However, the content is not updated. labelDailyCount should be updated to display the new daily count as UpdateDailyCount() is called correctly.

The record is correctly inserted into the database (as upon moving to a different page, and then back to the homepage, the record is then displayed). The timer continues to run when the user pauses the app, but on resuming the page, the UI is not updated.

If I put Device.BeginInvokeOnMainThread(() => UpdateDailyCount()); after Device.BeginInvokeOnMainThread(() => CalculateTimeDifference()); then the UI is updated correctly, but this is not ideal because UpdateDailyCount() would be called every second.

private Book latestBook;  

protected override void OnAppearing() 
{      
    base.OnAppearing();      
    latestBook = App.Database.GetRecentBookDate().FirstOrDefault();  
    UpdateDailyCount();
    Device.StartTimer(TimeSpan.FromSeconds(1), () =>       
    {          
       Device.BeginInvokeOnMainThread(() => CalculateTimeDifference());          
       return true; 
    }); 
}  

void BtnAdd_Clicked(object sender, EventArgs e) {       
     Book book = new Book        
     {           
         BookSaveTime = DateTime.Now       
     };       
     App.Database.SaveBook(book);       
     latestBook = book;
}  

void CalculateTimeDifference() {        
     if (latestBook == null) 
     {         
         this.labelTimeSince.Text = "-";        
     }        
     else        
     {           
         var timeDifference = DateTime.Now - latestBook.BookSaveTime;  
         this.labelTimeSince.Text = timeDifference.ToString("HH:mm:ss");        
     } 
}

void UpdateDailyCount() {        
     int dailyCount = App.Database.GetDailyCount();
     this.labelDailyCount.Text = dailyCount.ToString();
} 

Why is labelDailyCount.Text not updated when the page is resumed - despite UpdateDailyCount() being called correctly.?


Solution

  • Answer

    ContentPage.OnAppearing doesn't fire when a user resumes the app from the background.

    You'll want to override Application.OnResume to handle events when a user resumes the app from the background:

    public class App : Application
    {
        // ...
    
        protected override async void OnResume()
        { 
            base.OnResume();
    
            // Handle app resuming from background
        }
    
        // ...
    }
    

    Solution

    I recommend creating a Resumed event in Application and then you can subscribe to it from any ContentPage.

    public class App : Application
    {
        // ...
    
        public static event EventHandler Resumed;
    
        protected override async void OnResume()
        {
            base.OnResume();
    
            Resumed?.Invoke(this, EventArgs.Empty);
        }
    
        // ...
    }
    
    class BookPage : ContentPage
    {
        public BookContentPage()
        {
            InitializeComponent();
    
            App.Resumed += HandleResumed;
        }
    
        void HandleResumed(object sender, System.EventArgs e)
        {
            latestBook = App.Database.GetRecentBookDate().FirstOrDefault();  
            UpdateDailyCount();
            Device.StartTimer(TimeSpan.FromSeconds(1), () =>       
            {          
               Device.BeginInvokeOnMainThread(() => CalculateTimeDifference());          
              return true; 
            }); 
        }
    }