Issue with ObservableCollection in .net MAUI

Heinz Deubler 181 Reputation points
2022-07-13T19:49:26.597+00:00

When I open the app it reads the sqlite database and populates the ListView however when I add a new entry to the address book (by going to the "new address page") it doesn't refresh the ListView until I exit the app and come back in.

MainPage XAML

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"  
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
                 x:Class="MVVMAddressBook.Views.MainPage"  
                 xmlns:nav="clr-namespace:MVVMAddressBook.ViewModels"  
                 Title="MainPage">  
        <Grid>  
            <Grid.RowDefinitions>  
                <RowDefinition Height=".9*"></RowDefinition>  
                <RowDefinition Height=".1*"></RowDefinition>  
            </Grid.RowDefinitions>  
            <VerticalStackLayout Grid.Row="0">  
                <Label Text="Address Book"   
                   HorizontalOptions="Center">  
                </Label>  
                <ListView x:Name="listx"   
                          ItemsSource="{Binding AddressList}"  
                          VerticalScrollBarVisibility="Always"  
                          Grid.Row="0">  
                    <ListView.ItemTemplate>  
                        <DataTemplate>  
                            <ViewCell>  
                                <Grid Margin="10,0,10,0">  
                                    <Grid.RowDefinitions>  
                                        <RowDefinition Height="Auto" />  
                                    </Grid.RowDefinitions>  
                                    <Grid.ColumnDefinitions>  
                                        <ColumnDefinition Width="30*" />  
                                        <ColumnDefinition Width="30*" />  
                                        <ColumnDefinition Width="30*" />  
                                    </Grid.ColumnDefinitions>  
                                    <Label  Grid.Column="0" Text="{Binding ID}"/>  
                                    <Label  Grid.Column="1" Text="{Binding FirstName}"/>  
                                    <Label Grid.Column="2" Text="{Binding LastName}"/>  
                                </Grid>  
                            </ViewCell>  
                        </DataTemplate>  
                    </ListView.ItemTemplate>  
                </ListView>  
            </VerticalStackLayout>  
            <Button Text="Add New Address"  
                    HeightRequest="40"  
                    Grid.Row="1"   
                    Command="{Binding newAddressComand}"  
                    HorizontalOptions="Center">  
            </Button>  
        </Grid>  
    </ContentPage>  

MainPage code behind

using MVVMAddressBook.Models;  
using MVVMAddressBook.ViewModels;  
  
namespace MVVMAddressBook.Views;  
  
public partial class MainPage : ContentPage  
{  
 public MainPage()  
 {  
 InitializeComponent();                 
       BindingContext = new MainPageViewModel(Navigation);  
    }  
  
}  
  

ViewModel Page

namespace MVVMAddressBook.ViewModels  
{  
    [AddINotifyPropertyChangedInterface]  
    public class MainPageViewModel  
    {  
        public ICommand newAddressComand => new Command(() => GetCommand());  
        public INavigation Navigation { get; set; }  
  
        SQLiteConnection _connection;  
                  
        private ObservableCollection<Addresses> addressList;  
  
        public ObservableCollection<Addresses> AddressList  
        {  
            get { return addressList; }  
            set { addressList = value; }  
        }  
  
  
        public MainPageViewModel(INavigation navigation)  
        {            
            this.Navigation = navigation;  
            PopulateList();  
        }  
          
        // Go to new address page  
        public async void GetCommand()  
        {  
            await Navigation.PushAsync(new NewAddress());  
        }  
  
        public void PopulateList()  
        {  
            try  
            {                  
                var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "address.db");  
                _connection = new SQLiteConnection(dbPath);  
                var result = _connection.CreateTable<Addresses>();  
            }  
            catch (Exception ex)  
            {  
                Debug.WriteLine("Error = " + ex.Message);  
            }  
            AddressList = new ObservableCollection<Addresses>(_connection.Table<Addresses>().ToList());    
        }  
    }  
}  

after I add an address to the AddressList it doesn't show up in the ListView. Here is the code for the NewAddressViewModel.

Thank you :-)

namespace MVVMAddressBook.ViewModels  
{  
    [AddINotifyPropertyChangedInterface]  
    public class NewAddressViewModel : IContractAddresses  
    {  
        public ICommand saveCommand => new Command(() => GetSaveCommand());  
        public ICommand cancelCommand => new Command(() => GetCancelCommand());  
  
        public string FirstNameEntry { get; set; }    
        public string LastNameEntry { get; set; }  
        public string PhoneNumberEntry { get; set; }  
        public string AddressEntry { get; set; }  
        public string CityEntry { get; set; }  
        public string GenderEntry { get; set; }  
  
        SQLiteConnection _connection;  
        public INavigation Navigation { get; set; }  
        //Constructor  
        public NewAddressViewModel(INavigation navigation )  
        {  
            this.Navigation = navigation;  
            try  
            {  
                //Initialize database and create table  
                var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "address.db");  
                _connection = new SQLiteConnection(dbPath);  
                var result = _connection.CreateTable<Addresses>();  
            }  
            catch (Exception ex)  
            {  
                Debug.WriteLine("Error1 = " + ex.Message);  
            }  
        }  
  
        // Write to Database  
        public void GetSaveCommand()  
        {  
            Addresses addresses = new Addresses()  
            {  
                FirstName = FirstNameEntry,  
                LastName = LastNameEntry,  
                PhoneNumber = PhoneNumberEntry,  
                Address = AddressEntry,  
                City = CityEntry,  
                Gender = GenderEntry  
            };  
            try  
            {  
                var result = addresses;  
                var res = AddContact(addresses);  
                var res1 = _connection.Table<Addresses>().Count();  
                  
                var resk = GetContact(0);                  
            }  
            catch(Exception ex)  
            {  
                Debug.WriteLine("Error: " + ex.Message);   
            }  
        }  
  
  
        public void GetCancelCommand()  
        {  
            Debug.WriteLine("Cancel");  
            _connection.DropTable<Addresses>();  
            Navigation.PopAsync();  
        }  
          
  
        public Task<IEnumerable<Addresses>> GetContactsAsync()  
        {  
            throw new NotImplementedException();  
        }  
  
        public Addresses GetContact(int id)  
        {  
            try  
            {  
                var resu = _connection.Table<Addresses>().ToList();                  
            }  
            catch(Exception ex)  
            {  
                Debug.WriteLine("Get contact error = " + ex.Message);  
            }             
            return null;              
        }  
  
        public int AddContact(Addresses address)         
        {  
            try  
            {  
               int ret = _connection.Insert(address);  
                Debug.WriteLine("Insert error = " );  
                MainPageViewModel mainPageViewModel = new MainPageViewModel(null);  
                mainPageViewModel.PopulateList();  
            }  
            catch(Exception ex)  
            {       
                Debug.WriteLine("Insert error = " + ex.Message);                  
            }  
            return 0;  
        }  
  
        public Task UpdateContact(Addresses address)  
        {  
            throw new NotImplementedException();  
        }  
  
        public Task DeleteContact(Addresses address)  
        {  
            throw new NotImplementedException();  
        }  
          
    }  
}  

Address Model

public class Addresses  
    {  
        [PrimaryKey, AutoIncrement]  
        public int ID { get; set; }  
        public string FirstName { get; set; }  
        public string LastName { get; set; }  
        public string PhoneNumber { get; set; }  
        public string Address { get; set; }  
        public string City { get; set; }  
        public string Gender { get; set; }  
    }  

Thank you in advance
Heinz

.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
2,868 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,247 questions
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 68,651 Reputation points Microsoft Vendor
    2022-07-18T01:14:24.907+00:00

    Hello,​

    If you want to update the Listview when you navigated back to the MainPage. You need to add EventToCommandBehavior in your MainPage(Note: before use it, you need to install CommunityToolkit.Maui nuget package). When MainPage Appearing event is executed, then you can execute a command to get new data again in your MainPageViewModel.

    Here are steps to achieve it.

    Firstly, search CommunityToolkit.Maui in your Nuget Package manager (click Tools->Nuget Package manager->Manage nuget packages for solution). Install it, then open you MauiProgram.cs, add following code on CreateMauiApp method in order to initialize CommunityToolkit.Maui

       builder.UseMauiApp<App>().UseMauiCommunityToolkit();  
    

    Then We can add EventToCommandBehavior in MainPage. Add xmlns:behaviors prefix and ContentPage.Behaviors

       <ContentPage   
       ....  
                    xmlns:behaviors="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"  
       ....  
          >  
       <ContentPage.Behaviors>  
               <behaviors:EventToCommandBehavior EventName="Appearing" Command="{Binding PageAppearingCommand}">  
               </behaviors:EventToCommandBehavior>         
           </ContentPage.Behaviors>  
    

    In the end, achieve PageAppearingCommand method: clear AddressList, get the latest data from SQLite DB and populate the latest data to AddressList in MainPageViewModel.cs.

       public ICommand PageAppearingCommand => new Command(() => GetPopulateList());  
         
           private void GetPopulateList()  
           {  
               List<Addresses>  addresses= _connection.Table<Addresses>().ToList();  
               AddressList.Clear();  
               foreach (Addresses address in addresses)  
               {  
                   AddressList.Add(address);  
               }  
           }  
    

    Best Regards,

    Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.
    0 comments No comments

3 additional answers

Sort by: Most helpful
  1. Gordon Moore 6 Reputation points
    2022-07-20T16:24:07.93+00:00

    I don't know if this will help, but in your model you need to make the fields bindable as well even when using community toolkit.
    e.g here's an example from some code I used

       namespace TSDZ2Monitor.Models;  
         
       public partial class BluetoothData : ObservableObject  
       {  
         //Heart rate raw data  
         private int hRM;  
         public int HRM  
         {  
           get => hRM;  
           set => SetProperty(ref hRM, value);   //<---this bit is the important idea  
         
         }  
       ...  
       }  
    
    1 person found this answer helpful.

  2. Heinz Deubler 181 Reputation points
    2022-07-15T00:21:49.71+00:00

    Yes Viorel, after I add an address to the AddressList it doesn't show up in the ListView. Here is the code for the NewAddressViewModel.

    Thank you :-)

    namespace MVVMAddressBook.ViewModels  
    {  
        [AddINotifyPropertyChangedInterface]  
        public class NewAddressViewModel : IContractAddresses  
        {  
            public ICommand saveCommand => new Command(() => GetSaveCommand());  
            public ICommand cancelCommand => new Command(() => GetCancelCommand());  
      
            public string FirstNameEntry { get; set; }    
            public string LastNameEntry { get; set; }  
            public string PhoneNumberEntry { get; set; }  
            public string AddressEntry { get; set; }  
            public string CityEntry { get; set; }  
            public string GenderEntry { get; set; }  
      
            SQLiteConnection _connection;  
            public INavigation Navigation { get; set; }  
            //Constructor  
            public NewAddressViewModel(INavigation navigation )  
            {  
                this.Navigation = navigation;  
                try  
                {  
                    //Initialize database and create table  
                    var dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "address.db");  
                    _connection = new SQLiteConnection(dbPath);  
                    var result = _connection.CreateTable<Addresses>();  
                }  
                catch (Exception ex)  
                {  
                    Debug.WriteLine("Error1 = " + ex.Message);  
                }  
            }  
      
            // Write to Database  
            public void GetSaveCommand()  
            {  
                Addresses addresses = new Addresses()  
                {  
                    FirstName = FirstNameEntry,  
                    LastName = LastNameEntry,  
                    PhoneNumber = PhoneNumberEntry,  
                    Address = AddressEntry,  
                    City = CityEntry,  
                    Gender = GenderEntry  
                };  
                try  
                {  
                    var result = addresses;  
                    var res = AddContact(addresses);  
                    var res1 = _connection.Table<Addresses>().Count();  
                      
                    var resk = GetContact(0);                  
                }  
                catch(Exception ex)  
                {  
                    Debug.WriteLine("Error: " + ex.Message);   
                }  
            }  
      
      
            public void GetCancelCommand()  
            {  
                Debug.WriteLine("Cancel");  
                _connection.DropTable<Addresses>();  
                Navigation.PopAsync();  
            }  
              
      
            public Task<IEnumerable<Addresses>> GetContactsAsync()  
            {  
                throw new NotImplementedException();  
            }  
      
            public Addresses GetContact(int id)  
            {  
                try  
                {  
                    var resu = _connection.Table<Addresses>().ToList();                  
                }  
                catch(Exception ex)  
                {  
                    Debug.WriteLine("Get contact error = " + ex.Message);  
                }             
                return null;              
            }  
      
            public int AddContact(Addresses address)         
            {  
                try  
                {  
                   int ret = _connection.Insert(address);  
                    Debug.WriteLine("Insert error = " );  
                    MainPageViewModel mainPageViewModel = new MainPageViewModel(null);  
                    mainPageViewModel.PopulateList();  
                }  
                catch(Exception ex)  
                {       
                    Debug.WriteLine("Insert error = " + ex.Message);                  
                }  
                return 0;  
            }  
      
            public Task UpdateContact(Addresses address)  
            {  
                throw new NotImplementedException();  
            }  
      
            public Task DeleteContact(Addresses address)  
            {  
                throw new NotImplementedException();  
            }  
              
        }  
    }  
    

    Address Model

    public class Addresses  
        {  
            [PrimaryKey, AutoIncrement]  
            public int ID { get; set; }  
            public string FirstName { get; set; }  
            public string LastName { get; set; }  
            public string PhoneNumber { get; set; }  
            public string Address { get; set; }  
            public string City { get; set; }  
            public string Gender { get; set; }  
        }  
    

  3. Christian Campos 1 Reputation point
    2023-02-20T20:03:35.8633333+00:00

    I've having this issue with MAUI and I solved reassign the list in the command, for example:

        public ICommand EditCommand => new Command<MyClass>(Edit);
        private void Edit(MyClass detail)
        {
            if (detail != null)
            {
                this.prop1= 10; //change the value
                this.prop2=10; //change the value
    
               var det = Detail;
               Detail = null;
               Detail = det;  //here is the magic
            }
        }
    

    For some reason, the list (ObservableCollection in my case, I tried using a simple List but it doesn't refresh in all the cases) is updated in the page (View) only if I set it to null and reassign it.

    Why? I don't know, in Xamarin Forms I can modify some value in a record and it was refreshed.

    0 comments No comments