SQLite update UI in realtime

Eduardo Gomez 3,416 Reputation points
2021-03-21T22:25:32.13+00:00

Hello everyone

I have my basic app ready, but I have a few bugs. I am using MVVM, which state "No code-behind" (This is difficult because when yoy pass objects to another page, to need to catch them somehow)

Anyway, I am trying to delete Contacts from my DB, the contact will get deleted when I press delete, but the UI doesn't update, I have to reset the app to see the changes.

What I am doing wrong

Contacts UI

<ContentPage.ToolbarItems>
<ToolbarItem Text="New contact"
Command="{x:Binding AddContact}" />
</ContentPage.ToolbarItems>

<ContentPage.BindingContext>
    <vm:ContactsVM />
</ContentPage.BindingContext>

<ContentPage.Resources>
    <Style TargetType="cards:SfCardView">
        <Setter Property="VisualStateManager.VisualStateGroups">
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal" />
                    <VisualState x:Name="Selected">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor"
                                    Value="LightSkyBlue" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </Setter>
    </Style>
</ContentPage.Resources>

<CollectionView ItemsSource="{x:Binding Contacts}"
                SelectedItem="{x:Binding SelectedItem}"
                SelectionChangedCommand="{x:Binding SelectionItemCommand}"
                SelectionMode="Single">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <cards:SfCardView CornerRadius="5"
                              BackgroundColor="CornflowerBlue"
                              Margin="10,10,10,10"
                              Padding="10">
                <StackLayout>
                    <Label Text="{x:Binding Name, StringFormat='Name: {0}'}"
                           FontSize="14"
                           FontAttributes="Bold"/>
                    <Label Text="{x:Binding PhoneNumber, StringFormat='Phone: {0}'}"
                           Grid.Row="1" 
                           FontSize="10"/>
                    <Label Text="{x:Binding Address,  StringFormat='Address: {0}'}"
                           Grid.Row="2"
                           FontSize="10"/>
                </StackLayout>
            </cards:SfCardView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

Model

public class Contact : ViewModelBase {

    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }

    private string _Name;
    public string Name {
        get { return _Name; }
        set {
            if (_Name != value) {
                _Name = value;
                RaisePropertyChanged();
            }
        }
    }

    private string _PhoneNumber;
    public string PhoneNumber {
        get { return _PhoneNumber; }
        set {
            if (_PhoneNumber != value) {
                _PhoneNumber = value;
                RaisePropertyChanged();
            }
        }
    }

    private string _Address;
    public string Address {
        get { return _Address; }
        set {
            if (_Address != value) {
                _Address = value;
                RaisePropertyChanged();
            }
        }
    }
}

}

View Model Base

public class ViewModelBase : INotifyPropertyChanged {

    #region Notify Property Changed Members
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged([CallerMemberName] string propertyName = "") {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

DB Helper

namespace ContactXam.Service {
public class DBHelper {

    static SQLiteAsyncConnection db;
    static async Task init() {

        if (db != null) {
            return;
        }

        var databasePath = Path.Combine(FileSystem.AppDataDirectory, "MyContacts.db");

        db = new SQLiteAsyncConnection(databasePath);

        await db.CreateTableAsync<Person>();
    }

    public static async Task AddContact(string name, string phoneNumber, string address) {

        await init();

        Person person = new Person() {
            Name = name,
            PhoneNumber = phoneNumber,
            Address = address
        };

        var id = await db.InsertAsync(person);
    }
    public static async Task removeContact(int id) {

        await init();

        await db.DeleteAsync<Person>(id);
    }
    public static async Task<List<Person>> getContacts() {

        await init();

        var personList = await db.Table<Person>().ToListAsync();
        return personList;
    }
}

}

Contacts VM

 public ICommand AddContact { get; set; }

    private ObservableCollection<Person> _Contacts;
    public ObservableCollection<Person> Contacts {
        get { return _Contacts; }
        set {
            if (_Contacts != value) {
                _Contacts = value;
                RaisePropertyChanged();
            }
        }
    }


    private Person _SelectedItem;
    public Person SelectedItem {
        get { return _SelectedItem; }
        set {
            if (_SelectedItem != value) {
                _SelectedItem = value;
                RaisePropertyChanged();
            }
        }
    }
    public ICommand SelectionItemCommand { get; set; }


    public ContactsVM() {

        Populatedatabase();


        AddContact = new Command(async () => {
            var mainPage = Application.Current.MainPage;
            await mainPage.Navigation.PushAsync(new AddContact());
        });

        SelectionItemCommand = new Command(async () => {

            if (SelectedItem != null) {
                var mainPage = Application.Current.MainPage;
                string action = await mainPage.DisplayActionSheet(
                    "What do you want to do?:", "Cancel", "Delete", "Call", "Update", string.Empty);
                Debug.WriteLine("Action: " + action);

                switch (action) {
                    case "Call":

                        var status = await Xamarin.Essentials.
                        Permissions.RequestAsync<Xamarin.Essentials.Permissions.Phone>();

                        if (status != Xamarin.Essentials.PermissionStatus.Granted) {

                            status = await Xamarin.Essentials.
                            Permissions.RequestAsync<Xamarin.Essentials.Permissions.Phone>();
                        }

                        var phoneDialer = CrossMessaging.Current.PhoneDialer;
                        if (phoneDialer.CanMakePhoneCall)
                            phoneDialer.MakePhoneCall(SelectedItem.PhoneNumber);
                        break;
                    case "Update":
                        //await mainPage.Navigation.PushAsync(new UdateContactPage());
                        break;
                    case "Delete":
                        Contacts.Remove(SelectedItem);
                        await DBHelper.removeContact(SelectedItem.Id);
                        break;
                    default:
                        break;
                }
                SelectedItem = null;
            }
        });

    }




    public async void Populatedatabase() {

        Contacts = new ObservableCollection<Person>();
       var peopleList = await DBHelper.getContacts();
        Contacts.Clear();
        foreach (var item in peopleList) {
            Contacts.Add(item);
        }

    }
}

}

UI part for adding the contact

<ContentPage.BindingContext>
<vm:AddContactVM />
</ContentPage.BindingContext>

<ContentPage.Content>
    <Grid RowDefinitions="250, Auto"
          RowSpacing="20">
        <StackLayout BackgroundColor="AliceBlue">
            <Image Source="User" HeightRequest="200" WidthRequest="200" VerticalOptions="CenterAndExpand" />
        </StackLayout>
        <StackLayout Grid.Row="1"
                     Margin="20">
            <Entry Placeholder="Name"
                   Text="{x:Binding Contact.Name}" />
            <Entry Placeholder="Phone Number"
                   Text="{x:Binding Contact.PhoneNumber}"
                   Keyboard="Numeric"
                   MaxLength="10"/>
            <Entry Placeholder="Address"
                   Text="{x:Binding Contact.Address}" />
            <Button Grid.Row="2"
                    Margin="40"
                    Command="{x:Binding SaveCommand}"
                    Text="Save"/>
        </StackLayout>
    </Grid>
</ContentPage.Content>

</ContentPage>

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,292 questions
SQL Server
SQL Server
A family of Microsoft relational database management and analysis systems for e-commerce, line-of-business, and data warehousing solutions.
12,666 questions
0 comments No comments
{count} votes

Accepted answer
  1. JessieZhang-MSFT 7,706 Reputation points Microsoft Vendor
    2021-03-22T03:03:37.753+00:00

    Hello,

    Welcome to our Microsoft Q&A platform!

    When you tried to delete the seleted item by the following code, you can also try to remove the item form the Contacts . Since the type of Contacts is ObservableCollection<Contact> , once you delete the item from the Contacts , the UI will updated automatically.So you can remove the seleted item after you delete in your database.

        case "Delete":  
                         using (SQLiteConnection sQLiteConnection = new SQLiteConnection(App.DatabaseLocation)) {  
                             sQLiteConnection.CreateTable<Contact>();  
                             int row = sQLiteConnection.Delete(SelectedItem);  
                             if (row > 0) {  
                                 await mainPage.DisplayAlert("Deleted", "Element has been deleted successfully", "OK");  
                             }  
    
                            // delete the selected item in `Contacts `, then the UI will updated automatically.  
    
                          
                         }  
                         break;  
    

    In addition, I don't understand why you call 'ContactsVM = new ContactsVM(); 'to create a ContactsVM object and call function Populatedatabase() in method OnAppearing() on page ContactsPage . And I have found you have called function Populatedatabase(); in your ContactsVM.

    If I understand correctly, So you can set BindingContext for your page and set the ItemsSource for control to Contacts :

         public partial class MainViewXaml : ContentPage  
    {  
      
     public ContactsVM contactsVM { get; set; }  
      
    public MainViewXaml ()  
    {  
                           InitializeComponent();  
      
    contactsVM = new ContactsVM ();  
    BindingContext = contactsVM ;  
    }  
       }   
    

    And in your ContactsPage.xaml , set the ItemsSource for your ListView to Contacts

       <ListView  x:Name="lstView"   ItemsSource="{Binding Contacts }"  >  
       </ListView>  
    

    Update 1:

    You can refer to the following code in my demo:

    In the new contact page, , we can send a message to the contact list page :

            async void OnSaveClick(object sender, EventArgs e)  
        {  
            NewsarticleElements topicText = new NewsarticleElements();  
            topicText.topicTitle = TopicTitle.Text;  
            topicText.articleText = ArticleText.Text;  
    
    
            MessagingCenter.Send(this, "AddItem", topicText);  
            //await Navigation.PopAsync();  
            await Navigation.PopModalAsync();  
        }  
    

    And in the contact list page, we can Subscribe message as follows(my page is MainPage):

           public MainPage()  
        {  
            InitializeComponent();  
    
            //BindingContext = new MainPageViewModel();  
    
             MessagingCenter.Subscribe<NewPage, NewsarticleElements>(this, "AddItem", (obj, item) =>  
            {  
    
                var newItem = item as NewsarticleElements;  
                NewItem temp = new NewItem() { NewsName= newItem.topicTitle, ArticleText= newItem.articleText };  
    
                NewsItemList.Add(temp);  
    
                NewsView.ItemsSource = null;  
                NewsView.ItemsSource = NewsItemList;  
    
            });  
        }  
    

    Update 2

    You can try the following code:

    1. In ContactsVM.cs, Subscribe message in the constructor of class ContactsVM :
               public ContactsVM() {  
               Populatedatabase();  
      
               AddContact = new Command(async () => {  
                   var mainPage = Application.Current.MainPage;  
                   await mainPage.Navigation.PushAsync(new AddContact());  
               });  
      
      
               MessagingCenter.Subscribe<AddContactVM, Person>(this, "AddItem", (obj, item) =>  
               {  
                   Person newItem = item as Person;  
      
                   Contacts.Add(newItem);  
      
               });  
      
        // other code  
      
      }  
      
    2. send message in the constructor of class AddContactVM :
          public AddContactVM() {  
      
           person = new Person();  
      
           SaveCommand = new Command(async () => {  
      
               await DBHelper.AddContact(person.Name, person.PhoneNumber, person.Address);  
      
      
               // send message here  
               MessagingCenter.Send<AddContactVM, Person>(this, "AddItem", person);  
      
      
               var main = Application.Current.MainPage;  
      
               await main.Navigation.PopAsync();  
      
           });  
       }  
      

    Best Regards,

    Jessie Zhang

    ---
    If the response is helpful, please click "Accept Answer" and upvote it.

    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 additional answers

Sort by: Most helpful