I need to refine the CRUD methodology in a MVVM app in readiness to host SQLite database functionality

Grime 786 Reputation points
2021-05-19T05:09:45.117+00:00

With the help of @JessieZhang-MSFT and @Leon Lu (Shanghai Wicresoft Co,.Ltd.) , I have a working app that reads a short list of people ("Operators"), each of whom have a few properties: OperatorID, OperatorName, OperatorPhone, OperatorEmail and OperatorAvatar. At the moment, these are read in from the app but my goal is to have them populate a locally stored SQLite database table called Operators. Currently there is CRUD functionality, but it is spread around a number of different classes in the app.
To make the app more readable and logical, I would like to bring the CRUD functionality into the OperatorViewModel.cs program where the Create (or Add), Read, Update and Delete functions can then be amended to cater to the SQLite database.
The structure at the moment is that MainPage (level 1) calls MenuPage (level 2) which calls OperatorsPage (level 3) where the CRUD functionality occurs in a number of other pages (level 4).
The data model is set up in OperatorModel.cs and the list is populated in OperatorViewModel.cs, where I'd eventually like the CRUD methods to reside.
I have started to code the database in HFNDatabase.cs but am running into all sorts of difficulties.
I'll include the code here...

OperatorModel.cs

using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Runtime.CompilerServices;  
using System.Text;  
using SQLite;  
  
namespace HandsFreeNotes.Model  
{  
    //    public class OperatorModel  
    // The line above changed to the line below at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
    public class OperatorModel: INotifyPropertyChanged  
    {  
        public int OperatorID { get; set; }  
        //        public string OperatorName { get; set; }  
        // The 1 line above changed to the 6 lines below at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
        string _operatorName;  
        public string OperatorName  
        {  
            set { SetProperty(ref _operatorName, value); }  
            get { return _operatorName; }  
        }  
  
        public string OperatorPhone { get; set; }  
        public string OperatorEmail { get; set; }  
        public string OperatorAvatar { get; set; }  
  
        // The 4 lines below added at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
        public OperatorModel()  
        {  
  
        }  
  
        public OperatorModel(int operatorID, string operatorName, string operatorPhone, string operatorEmail, string operatorAvatar)  
        {  
            OperatorID = operatorID;  
            OperatorName = operatorName;  
            OperatorPhone = operatorPhone;  
            OperatorEmail = operatorEmail;  
            OperatorAvatar = operatorAvatar;  
        }  
        // All the lines below added at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)  
        {  
            if (Object.Equals(storage, value))  
                return false;  
  
            storage = value;  
            OnPropertyChanged(propertyName);  
            return true;  
        }  
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)  
        {  
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
        }  
  
        public event PropertyChangedEventHandler PropertyChanged;  
    }  
  
  
}  

OperatorViewModel.cs

using HandsFreeNotes.Model;  
using System;  
using System.Collections.Generic;  
using System.Collections.ObjectModel;  
using System.ComponentModel;  
using System.Text;  
using System.Threading.Tasks;  
using Xamarin.Forms;  
using SQLite;  
  
namespace HandsFreeNotes.ViewModel  
{  
    public class OperatorViewModel : INotifyPropertyChanged  
    {  
        private string _HFNName;  
  
        public string HFNName  
        {  
            get  
            {  
                return _HFNName;  
            }  
              
            set  
            {  
                _HFNName = value;  
                RaisePropertyChanged("HFNName");  
            }  
        }  
  
        // public List<OperatorModel> OperatorList;  
        // The line above replaced by the 5 lines below at the behest of LeonLu-MSFT (Microsoft Q&A) 06/05/2021  
        public OperatorViewModel()  
        {  
            GetOperators();  
        }  
        public static ObservableCollection<OperatorModel> OperatorList;  
  
  
        public OperatorModel SelectedOperator;  
  
        public async Task<bool> GetOperators()  
        {  
            // OperatorList = await App.Database.GetOperatorsAsync();  
  
            if (OperatorList == null)  
            {  
                // OperatorList = new List<OperatorModel>();  
                // The line above replaced by the line below at the behest of LeonLu-MSFT (Microsoft Q&A) 06/05/2021  
                OperatorList = new ObservableCollection<OperatorModel>();  
  
                OperatorList.Add(new OperatorModel(1, "Grime", "61419659866", "apps@grime.net", "gs300.png"));  
                OperatorList.Add(new OperatorModel(2, "Brookey", "61419659866", "brookey@grime.net", "gmb.jpg"));  
                OperatorList.Add(new OperatorModel(3, "CC", "61422192456", "shaunachick@gmail.com", "cc.jpg"));  
                OperatorList.Add(new OperatorModel(4, "Monte", "61412595878", "monte@aussieweb.com.au", "monte2a.jpg"));  
                OperatorList.Add(new OperatorModel(5, "Jeff", "61409998197", "jeff@content2convert.com.au", "jeff.jpg"));  
            }  
  
            return true;  
        }  
  
        public bool UpdateOperator(OperatorModel opm)  
        {  
            SelectedOperator.OperatorID = opm.OperatorID;  
            SelectedOperator.OperatorName = opm.OperatorName;  
            SelectedOperator.OperatorPhone = opm.OperatorPhone;  
            SelectedOperator.OperatorEmail = opm.OperatorEmail;  
            SelectedOperator.OperatorAvatar = opm.OperatorAvatar;  
                          
            return true;  
        }  
  
        public event PropertyChangedEventHandler PropertyChanged;  
  
        protected void RaisePropertyChanged(string prop)  
        {  
            if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }  
        }  
  
    }  
}  

HFNDatabase.cs

using HandsFreeNotes.Model;  
using SQLite;  
using System;  
using System.Collections.Generic;  
using System.Text;  
using System.Threading.Tasks;  
  
// This class added according to Cary Baer:  
// https://www.skillshare.com/classes/Mobile-Application-Development-With-Cross-Platform-Xamarin-Forms/984679771/projects  
  
namespace HandsFreeNotes.Data  
{  
    public class HFNDatabase  
    {  
        readonly SQLiteAsyncConnection database;  
  
        public HFNDatabase(string dbPath)  
        {  
            database = new SQLiteAsyncConnection(dbPath);  
            database.CreateTableAsync<OperatorModel>().Wait();  
        }  
  
        public async Task<List<OperatorModel>> GetOperatorsAsync()  
        {  
            return await database.Table<OperatorModel>().ToListAsync();  
        }  
  
        public async Task<int> InsertOperator(OperatorModel om)  
        {  
            await database.InsertAsync(om);  
            int ret = 1;  
            return ret;  
        }  
  
        public async Task<int> DeleteOperator(OperatorModel om)  
        {  
            await database.ExecuteScalarAsync<OperatorModel>("DELETE FROM [OperatorModel] WHERE TITLE = ?", om.OperatorName);  
            int ret = 1;  
            return ret;  
        }  
  
        public async Task<int> UpdatOperator(OperatorModel om)  
        {  
            await database.ExecuteScalarAsync<OperatorModel>("UPDATE [OperatorModel] SET OPERATORID = ?, OPERATORPHONE = ?, OPERATOREMAIL = ?, OPERATORAVATAR = ? WHERE TITLE = ?", om.OperatorID, om.OperatorPhone, om.OperatorEmail, om.OperatorAvatar, om.OperatorName);  
            int ret = 1;  
            return ret;  
        }  
    }  
}  

MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>  
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
             BackgroundColor="White"  
             Title="Hands Free Notes"  
             x:Class="HandsFreeNotes.MainPage">  
  
    <Grid BackgroundColor="White">  
        <Grid.RowDefinitions>  
            <RowDefinition Height="40"/>  
            <RowDefinition Height="40"/>  
            <RowDefinition Height="80"/>  
            <RowDefinition Height="*"/>  
            <RowDefinition Height="50"/>  
        </Grid.RowDefinitions>  
  
        <Grid.ColumnDefinitions>  
            <ColumnDefinition Width="50"/>  
            <ColumnDefinition Width="*"/>  
            <ColumnDefinition Width="50"/>  
        </Grid.ColumnDefinitions>  
  
        <Button  
            x:Name="AboutButton"  
            Text="About"  
            TextColor="Black"  
            FontSize="Medium"  
            FontAttributes="Bold"  
            Grid.Row="0"  
            Grid.Column="0"  
            Grid.ColumnSpan="2"  
            Margin="20, 0, 0, 0"  
            VerticalOptions="Center"  
            HorizontalOptions="Start"  
            Clicked="AboutButton_Clicked"/>  
  
        <Button  
            x:Name="MenuButton"  
            ImageSource="hbb32"  
            Grid.Row="0"  
            Grid.Column="1"  
            Grid.ColumnSpan="2"  
            Margin="0, 0, 20, 0"  
            VerticalOptions="Center"  
            HorizontalOptions="End"  
            BackgroundColor="Transparent"  
            Clicked="MenuButton_Clicked"/>  
  
        <Image   
            Source="hfn256"  
            Margin="0, 0, 0, 0"  
            Grid.Row="1"   
            Grid.Column="0"  
            Grid.ColumnSpan="3"  
            HorizontalOptions="Center"  
            VerticalOptions="Center"  
            BackgroundColor="Transparent"/>  
  
        <Label  
            Text="Home"  
            TextColor="Black"  
            Grid.Row="2"  
            Grid.Column="1"  
            FontSize="24"  
            FontAttributes="Bold"  
            HorizontalOptions="Center"  
            VerticalOptions="Center"/>  
  
        <ScrollView Orientation="Vertical"  
            Grid.Row="3" Grid.Column="0"  
            Grid.ColumnSpan="3">  
            <Grid BackgroundColor="White" >  
                <Grid.RowDefinitions>  
                    <RowDefinition Height="50"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="80"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="80"/>  
                    <RowDefinition Height="80"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="60"/>  
                    <RowDefinition Height="*"/>  
                </Grid.RowDefinitions>  
  
                <Grid.ColumnDefinitions>  
                    <ColumnDefinition Width="150"/>  
                    <ColumnDefinition Width="*"/>  
                    <ColumnDefinition Width="*"/>  
                    <ColumnDefinition Width="10"/>  
                </Grid.ColumnDefinitions>  
  
                <Label  
                    x:Name="OperatorLabel"  
                    Text="Operator:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="0"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,0,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="OperatorButton"  
                    Text="Click to SET Operator"  
                    Grid.Row="0"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    BackgroundColor="White"  
                    Margin="10,0,0,0"  
                    Clicked="OperatorButton_Clicked"  
                    VerticalOptions="Center"/>  
  
                <Label  
                    x:Name="RecipientLabel"  
                    Text="Recipient:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="1"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,20,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="RecipientButton"  
                    Text="Click to SET Recipient"  
                    Grid.Row="1"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    Margin="10,20,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"/>  
  
                <Label  
                    x:Name="SubjectLabel"  
                    Text="Subject:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="2"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,20,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="SubjectButton"  
                    Text="Click to SET Subject"  
                    Grid.Row="2"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    Margin="10,20,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"/>  
  
                <Label  
                    x:Name="InstanceStartLabel"  
                    Text="Instance&#x0a;      Start:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="3"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,20,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="InstanceStartButton"  
                    Text="Click to START Instance"  
                    Grid.Row="3"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    Margin="10,20,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"/>  
  
                <Label  
                    x:Name="AudioLabel"  
                    Text="Audio:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="4"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,20,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="AudioButton"  
                    Text="Click to RECORD Audio Notes"  
                    Grid.Row="4"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    Margin="10,20,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"/>  
  
                <Label  
                    x:Name="MediaLabel"  
                    Text="Photos&#x0a;&amp; Video:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="5"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,20,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="MediaButton"  
                    Text="Click to SELECT Media"  
                    Grid.Row="5"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    Margin="10,20,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"/>  
  
                <Label  
                    x:Name="InstanceStopLabel"  
                    Text="Instance&#x0a;       Stop:"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="6"  
                    Grid.Column="0"  
                    BackgroundColor="Transparent"  
                    Margin="10,20,0,0"  
                    HorizontalOptions="End"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="InstanceStopButton"  
                    Text="Click to STOP Instance"  
                    Grid.Row="6"  
                    Grid.Column="1"  
                    Grid.ColumnSpan="2"  
                    TextColor="Gray"  
                    FontAttributes="Italic"  
                    FontSize="Small"  
                    Margin="10,20,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"/>  
  
                <Button  
                    x:Name="SendButton"  
                    Text="Send Instance Now"  
                    FontSize="Large"  
                    FontAttributes="Bold"  
                    Grid.Row="8"  
                    Grid.Column="0"  
                    Grid.ColumnSpan="3"  
                    TextColor="DarkBlue"  
                    Margin="10,0,0,0"  
                    BackgroundColor="White"  
                    VerticalOptions="Center"  
                    Clicked="SendButton_Clicked"/>  
  
            </Grid>  
  
        </ScrollView>  
  
    </Grid>  
</ContentPage>  

MainPage.xaml.cs

using System;  
using System.Collections.Generic;  
using System.ComponentModel;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
using Xamarin.Forms;  
using SQLite;  
using HandsFreeNotes.Model;  
using HandsFreeNotes.View;  
using HandsFreeNotes.ViewModel;  
using Xamarin.Essentials;  
  
namespace HandsFreeNotes  
{  
    public partial class MainPage : ContentPage  
    {  
        OperatorViewModel ovm;  
        // The line below added at the behest of LeonLu-MSFT (Microsoft Q&A) 13/05/2021  
        OperatorModel opm;  
  
        public MainPage()  
        {  
            // add a bit of padding to cater to the "notch" on the iPhone.  
            if (Device.RuntimePlatform == Device.iOS) {Padding = new Thickness(0, 40, 0, 0);}  
  
            InitializeComponent();  
  
            ovm = new OperatorViewModel();  
            // The line below added at the behest of LeonLu-MSFT (Microsoft Q&A) 13/05/2021  
            opm = new OperatorModel();  
  
            BindingContext = ovm;  
        }  
  
        //protected override async void OnAppearing()  
        //{  
        //    base.OnAppearing();  
        //    await RefreshOperatorListView();  
        //}  
  
        //public async Task<bool> RefreshOperatorListView()  
        //{  
        //    OperatorListView.ItemSource = null;  
        //    await ovm.GetOperators()  
        //    OperatorListView.ItemSource = ovm.OperatorList;  
        //    return true;  
        //}  
  
        private async void OperatorButton_Clicked(object sender, EventArgs e)  
        {  
            // await Navigation.PushModalAsync(new OperatorsPage());  
            // The line above changed to the lines below (including BackCall) at the behest of LeonLu-MSFT (Microsoft Q&A) 13/05/2021  
            var page = new OperatorsPage(SelectModel);  
            page.ReturnValue += delegate (object s, OperatorModel operatorModel)  
            {  
                BackCall(s, operatorModel);  
            };  
            await Navigation.PushModalAsync(page);  
        }  
  
        OperatorModel SelectModel;  
  
        private void BackCall(object s, OperatorModel model)  
        {  
            SelectModel = model;  
            opm.OperatorName = model.OperatorName;  
            OperatorButton.Text = opm.OperatorName;  
            OperatorButton.TextColor = Color.Green;  
            OperatorButton.FontSize = 20;  
        }  
  
        private async void AboutButton_Clicked(object sender, EventArgs e)  
        {  
            await Navigation.PushModalAsync(new AboutPage());  
        }  
  
        private async void MenuButton_Clicked(object sender, EventArgs e)  
        {  
            var page = new MenuPage(SelectModel);  
            page.ReturnValue += delegate (object s, OperatorModel operatorModel)  
            {  
                BackCall(s, operatorModel);  
            };  
            await Navigation.PushModalAsync(page);  
  
        }  
  
        private async void SendButton_Clicked(object sender, EventArgs e)  
        {  
            await Email.ComposeAsync();  
        }  
    }  
}  

MenuPage.xaml

<?xml version="1.0" encoding="utf-8" ?>  
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
             x:Class="HandsFreeNotes.View.MenuPage">  
    <ContentPage.Content>  
        <Grid BackgroundColor="White">  
            <Grid.RowDefinitions>  
                <RowDefinition Height="40"/>  
                <RowDefinition Height="40"/>  
                <RowDefinition Height="60"/>  
                <RowDefinition Height="*"/>  
                <RowDefinition Height="50"/>  
            </Grid.RowDefinitions>  
  
            <Grid.ColumnDefinitions>  
                <ColumnDefinition Width="50"/>  
                <ColumnDefinition Width="*"/>  
                <ColumnDefinition Width="50"/>  
            </Grid.ColumnDefinitions>  
  
            <Button  
                x:Name="BackButton"  
                Text="Back"  
                Grid.Row="0"  
                Grid.Column="0"  
                Grid.ColumnSpan="3"  
                TextColor="Black"  
                FontAttributes="Bold"  
                HeightRequest="40"  
                VerticalOptions="Center"  
                HorizontalOptions="End"  
                Margin="0,0,20,0"  
                Clicked="BackButton_Clicked"/>  
  
            <Image   
                Source="hfn256"  
                Margin="2"  
                Grid.Row="1"   
                Grid.Column="0"  
                Grid.ColumnSpan="3"  
                HorizontalOptions="Center"  
                BackgroundColor="Transparent"/>  
  
            <Label   
                Text="Menu"  
                FontSize="Large"  
                TextColor="Black"  
                FontAttributes="Bold"  
                Margin =" 0, 0, 0, 0"  
                Grid.Row="2"   
                Grid.Column="0"  
                Grid.ColumnSpan="3"  
                HorizontalOptions="Center"  
                VerticalOptions="Center"  
                BackgroundColor="Transparent"/>  
  
            <ScrollView   
                Orientation="Vertical"  
                Grid.Row="3"   
                Grid.Column="0"  
                Grid.ColumnSpan="3">  
                <Grid BackgroundColor="White" >  
                    <Grid.RowDefinitions>  
                        <RowDefinition Height="60"/>  
                        <RowDefinition Height="60"/>  
                        <RowDefinition Height="60"/>  
                        <RowDefinition Height="60"/>  
                        <RowDefinition Height="60"/>  
                        <RowDefinition Height="60"/>  
                    </Grid.RowDefinitions>  
  
                    <Grid.ColumnDefinitions>  
                        <ColumnDefinition Width="50"/>  
                        <ColumnDefinition Width="*"/>  
                        <ColumnDefinition Width="50"/>  
                    </Grid.ColumnDefinitions>  
  
                    <Button  
                        x:Name="ManageOperatorsButton"  
                        Text="Manage Operators"  
                        FontSize="Medium"  
                        Grid.Row="0"  
                        Grid.Column="0"  
                        Grid.ColumnSpan="3"  
                        TextColor="Black"  
                        FontAttributes="Bold"  
                        BackgroundColor="White"  
                        Margin="0,20,0,0"  
                        HorizontalOptions="Center"  
                        VerticalOptions="Start"  
                        Clicked="ManageOperatorsButton_Clicked"/>  
  
                    <Button  
                        x:Name="ManageRecipientsButton"  
                        Text="Manage Recipients"  
                        FontSize="Medium"  
                        Grid.Row="1"  
                        Grid.Column="0"  
                        Grid.ColumnSpan="3"  
                        TextColor="Black"  
                        FontAttributes="Bold"  
                        BackgroundColor="White"  
                        Margin="0,20,0,0"  
                        HorizontalOptions="Center"  
                        VerticalOptions="Start"/>  
  
                    <Button  
                        x:Name="ManageSubjectsButton"  
                        Text="Manage Subjects"  
                        FontSize="Medium"  
                        Grid.Row="2"  
                        Grid.Column="0"  
                        Grid.ColumnSpan="3"  
                        TextColor="Black"  
                        FontAttributes="Bold"  
                        BackgroundColor="White"  
                        Margin="0,20,0,0"  
                        HorizontalOptions="Center"  
                        VerticalOptions="Start"/>  
  
                    <Button  
                        x:Name="ManageInstancesButton"  
                        Text="Manage Instances"  
                        FontSize="Medium"  
                        Grid.Row="3"  
                        Grid.Column="0"  
                        Grid.ColumnSpan="3"  
                        TextColor="Black"  
                        FontAttributes="Bold"  
                        BackgroundColor="White"  
                        Margin="0,20,0,0"  
                        HorizontalOptions="Center"  
                        VerticalOptions="Start"/>  
  
                    <Button  
                        x:Name="ManageAudioRecordingsButton"  
                        Text="Manage Audio Recordings"  
                        FontSize="Medium"  
                        Grid.Row="4"  
                        Grid.Column="0"  
                        Grid.ColumnSpan="3"  
                        TextColor="Black"  
                        FontAttributes="Bold"  
                        BackgroundColor="White"  
                        Margin="0,20,0,0"  
                        HorizontalOptions="Center"  
                        VerticalOptions="Start"/>  
  
                </Grid>  
  
            </ScrollView>  
  
        </Grid>    </ContentPage.Content>  
</ContentPage>  

MenuPage.xaml.cs

using HandsFreeNotes.Model;  
using HandsFreeNotes.ViewModel;  
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Threading.Tasks;  
  
using Xamarin.Forms;  
using Xamarin.Forms.Xaml;  
  
namespace HandsFreeNotes.View  
{  
    [XamlCompilation(XamlCompilationOptions.Compile)]  
    public partial class MenuPage : ContentPage  
    {  
  
        OperatorViewModel ovm;  
        // The line below added at the behest of LeonLu-MSFT (Microsoft Q&A) 14/05/2021  
        OperatorModel SelectModel;  
  
        // public MenuPage()  
        // The line above replaced with the line below at the behest of LeonLu-MSFT (Microsoft Q&A) 14/05/2021  
        public MenuPage(OperatorModel SelectModel)  
        {  
            // add a bit of padding to cater to the "notch" on the iPhone.  
            if (Device.RuntimePlatform == Device.iOS) {Padding = new Thickness(0, 40, 0, 0);}  
  
            InitializeComponent();  
  
            // The line below added at the behest of Leo
Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,296 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,799 questions
{count} votes

Accepted answer
  1. JessieZhang-MSFT 7,706 Reputation points Microsoft Vendor
    2021-05-22T09:15:22.597+00:00

    Hello,

    Welcome to our Microsoft Q&A platform!

    Based on your code, I created a demo and modified your database HFNDatabase . The insert and

    You can refer to the following code:

    1. HFNDatabase.cs public class HFNDatabase
      {
      static SQLiteAsyncConnection database;
          public static readonly AsyncLazy<HFNDatabase> Instance = new AsyncLazy<HFNDatabase>(async () =>  
          {  
              var instance = new HFNDatabase();  
              CreateTableResult result = await database.CreateTableAsync<Models.OperatorModel>();  
              return instance;  
          });  
      
          public HFNDatabase()  
          {  
              database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);  
          }  
      
          public Task<List<Models.OperatorModel>> GetOperatorsAsync()  
          {  
              return database.Table<Models.OperatorModel>().ToListAsync();  
          }  
      
          public Task<int> DeleteItemAsync(Models.OperatorModel item)  
          {  
              return database.DeleteAsync(item);  
          }  
      
          public Task<int> SaveItemAsync(Models.OperatorModel item)  
          {  
              if (item.OperatorID != 0)  
              {  
                  return database.UpdateAsync(item);  
              }  
              else  
              {  
                  return database.InsertAsync(item);  
              }  
          }  
      }  
      

    Constants.cs

     public static class Constants  
    {  
        public const string DatabaseFilename = "OperatorsSQLite.db3";  
      
        public const SQLite.SQLiteOpenFlags Flags =  
            // open the database in read/write mode  
            SQLite.SQLiteOpenFlags.ReadWrite |  
            // create the database if it doesn't exist  
            SQLite.SQLiteOpenFlags.Create |  
            // enable multi-threaded database access  
            SQLite.SQLiteOpenFlags.SharedCache;  
      
        public static string DatabasePath  
        {  
            get  
            {  
                var basePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);  
                return Path.Combine(basePath, DatabaseFilename);  
            }  
        }  
    }  
    

    AsyncLazy.cs

    public class AsyncLazy<T> : Lazy<Task<T>>  
    {  
        readonly Lazy<Task<T>> instance;  
    
        public AsyncLazy(Func<T> factory)  
        {  
            instance = new Lazy<Task<T>>(() => Task.Run(factory));  
        }  
    
        public AsyncLazy(Func<Task<T>> factory)  
        {  
            instance = new Lazy<Task<T>>(() => Task.Run(factory));  
        }  
    
        public TaskAwaiter<T> GetAwaiter()  
        {  
            return instance.Value.GetAwaiter();  
        }  
    }  
    

    OperatorModel.cs

    public class OperatorModel: INotifyPropertyChanged  
    {  
        [PrimaryKey, AutoIncrement]  
        public int OperatorID { get; set; }  
    
        string _operatorName;  
        public string OperatorName  
        {  
            set { SetProperty(ref _operatorName, value); }  
    
            get { return _operatorName; }  
        }  
    
    
        public string OperatorPhone { get; set; }  
        public string OperatorEmail { get; set; }  
        public string OperatorAvatar { get; set; }  
    
        bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)  
        {  
            if (Object.Equals(storage, value))  
                return false;  
    
            storage = value;  
            OnPropertyChanged(propertyName);  
            return true;  
        }  
    
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)  
        {  
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
        }  
    
        public event PropertyChangedEventHandler PropertyChanged;  
    }  
    
    1. In page SelectOperatorPage.xaml, you can use data binding :
       <ContentPage.Content>  
           <Grid BackgroundColor="White">  
               <Grid.RowDefinitions>  
                   <RowDefinition Height="40"/>  
                   <RowDefinition Height="40"/>  
                   <RowDefinition Height="80"/>  
                   <RowDefinition Height="*"/>  
                   <RowDefinition Height="50"/>  
               </Grid.RowDefinitions>  
      
               <Grid.ColumnDefinitions>  
                   <ColumnDefinition Width="50"/>  
                   <ColumnDefinition Width="*"/>  
                   <ColumnDefinition Width="50"/>  
               </Grid.ColumnDefinitions>  
      
               <Button  
                    x:Name="BackButton"  
                    Text="Back"  
                    Grid.Row="0"  
                    Grid.Column="0"  
                    Grid.ColumnSpan="3"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    HeightRequest="40"  
                    VerticalOptions="CenterAndExpand"  
                    HorizontalOptions="End"  
                    Margin="0,0,20,0"  
                    Clicked="BackButton_Clicked"/>  
      
               <Image   
                    Source="hfn256"  
                    Margin="2"  
                    Grid.Row="1"   
                    Grid.Column="0"  
                    Grid.ColumnSpan="3"  
                    HorizontalOptions="Center"  
                    BackgroundColor="Transparent"/>  
      
               <Label   
                    Text="Select Operator..."  
                    FontSize="Large"  
                    TextColor="Black"  
                    FontAttributes="Bold"  
                    Grid.Row="2"   
                    Grid.Column="0"  
                    Grid.ColumnSpan="3"  
                    HorizontalOptions="Center"  
                    VerticalOptions="Start"  
                    Margin="20"  
                    BackgroundColor="Transparent"/>  
      
               <ListView  
                    x:Name="OperatorListView"  
                    Grid.Row="3"  
                    Grid.Column="0"  
                    Grid.ColumnSpan="3"  
                    RowHeight="140"  
                    ItemSelected="OperatorListView_ItemSelected">  
                   <ListView.ItemTemplate>  
                       <DataTemplate>  
                           <ViewCell>  
                               <ContentView>  
                                   <Grid>  
                                       <Grid.RowDefinitions>  
                                           <RowDefinition Height="*"/>  
                                       </Grid.RowDefinitions>  
                                       <Grid.ColumnDefinitions>  
                                           <ColumnDefinition Width="100"/>  
                                           <ColumnDefinition Width="100"/>  
                                           <ColumnDefinition Width="200"/>  
                                       </Grid.ColumnDefinitions>  
      
                                       <Image  
                                            Source="{Binding OperatorAvatar}"  
                                            Grid.Row="0"  
                                            Grid.Column="0"  
                                            HeightRequest="200"  
                                            WidthRequest="100"/>  
      
                                       <Label  
                                            Text="{Binding OperatorName}"  
                                            FontAttributes="Bold"  
                                            HorizontalOptions="Start"  
                                            VerticalOptions="Center"  
                                            Grid.Row ="0"  
                                            Grid.Column="1"/>  
      
                                       <Label  
                                            Text="{Binding OperatorEmail}"  
                                            FontAttributes="Italic"  
                                            FontSize="Micro"  
                                            Grid.Row="0"  
                                            Grid.Column="2"  
                                            HeightRequest="30"  
                                            HorizontalOptions="Start"  
                                            VerticalOptions="Center"/>  
      
                                       <Label  
                                            Text="{Binding OperatorPhone}"  
                                            FontAttributes="Italic"  
                                            FontSize="Small"  
                                            Grid.Row="0"  
                                            Grid.Column="2"  
                                            HeightRequest="30"  
                                            HorizontalOptions="Start"  
                                            VerticalOptions="End"/>  
      
                                   </Grid>  
                               </ContentView>  
                           </ViewCell>  
                       </DataTemplate>  
                   </ListView.ItemTemplate>  
               </ListView>  
      
           </Grid>  
       </ContentPage.Content>  
      

    SelectOperatorPage.xaml.cs

       public partial class SelectOperatorPage : ContentPage  
        {  
            OperatorViewModel ovm;  
            public EventHandler<Models.OperatorModel> ReturnValue;  
      
            public SelectOperatorPage()  
            {  
                // add a bit of padding to cater to the "notch" on the iPhone.  
                if (Device.RuntimePlatform == Device.iOS)  
                {  
                    Padding = new Thickness(0, 40, 0, 0);  
                }  
      
                InitializeComponent();  
            }  
      
            protected override async void OnAppearing()  
            {  
                base.OnAppearing();  
                //await ovm.GetOperators();  
                //OperatorListView.ItemsSource = OperatorViewModel.OperatorList;  
      
                HFNDatabase database = await HFNDatabase.Instance;  
                OperatorListView.ItemsSource = await database.GetOperatorsAsync();  
      
            }  
      
            private async void BackButton_Clicked(object sender, EventArgs e)  
            {  
                await Navigation.PopModalAsync();  
            }  
      
            private async void OperatorListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)  
            {  
                if (e.SelectedItem != null)  
                {  
      
                    Models.OperatorModel operatorModel = (Models.OperatorModel)e.SelectedItem;  
                    //ovm.SelectedOperator = operatorModel;  
                    EventHandler<Models.OperatorModel> handler = ReturnValue;  
                    if (handler != null)  
                    {  
                        handler(this, operatorModel);  
                    }  
      
                    await Navigation.PopModalAsync();  
                }  
            }  
        }  
    
    1. In NewOperatorPage.xaml.cs,we can create a new Operator by code await database.SaveItemAsync(todoItem);
      [XamlCompilation(XamlCompilationOptions.Compile)]  
       public partial class NewOperatorPage : ContentPage  
       {  
           public NewOperatorPage()  
           {  
               // add a bit of padding to cater to the "notch" on the iPhone.  
               if (Device.RuntimePlatform == Device.iOS)  
               {  
                   Padding = new Thickness(0, 40, 0, 0);  
               }  
      
               InitializeComponent();  
           }  
      
           private async void BackButton_Clicked(object sender, EventArgs e)  
           {  
               await Navigation.PopModalAsync();  
           }  
      
           private async void OKButton_Clicked(object sender, EventArgs e)  
           {  
               //var name = NameEntry.Text;  
               //var Phone = PhoneEntry.Text;  
               //var Email = EmailEntry.Text;  
               //var Avatar = AvatarEntry.Text;  
               //OperatorViewModel.OperatorList.Add(new OperatorModel(OperatorViewModel.OperatorList.Count, name, Phone, Email, Avatar));  
               //await Navigation.PopModalAsync();  
      
               var todoItem = (Models.OperatorModel)BindingContext;  
               HFNDatabase database = await HFNDatabase.Instance;  
               await database.SaveItemAsync(todoItem);  
               await Navigation.PopModalAsync();  
           }  
      
           private async void CancelButton_Clicked(object sender, EventArgs e)  
           {  
               await Navigation.PopModalAsync();  
           }  
       }  
      

    In NewOperatorPage.xaml, we can also use data binding for the several Entry.

    <ContentPage.Content>  
            <Grid BackgroundColor="White">  
                <Grid.RowDefinitions>  
                    <RowDefinition Height="40"/>  
                    <RowDefinition Height="40"/>  
                    <RowDefinition Height="30"/>  
                    <RowDefinition Height="10"/>  
                    <RowDefinition Height="*"/>  
                    <RowDefinition Height="50"/>  
                </Grid.RowDefinitions>  
      
                <Grid.ColumnDefinitions>  
                    <ColumnDefinition Width="50"/>  
                    <ColumnDefinition Width="*"/>  
                    <ColumnDefinition Width="50"/>  
                </Grid.ColumnDefinitions>  
      
                <Button  
                     x:Name="BackButton"  
                     Text="Back"  
                     Grid.Row="0"  
                     Grid.Column="0"  
                     Grid.ColumnSpan="3"  
                     TextColor="Black"  
                     FontAttributes="Bold"  
                     HeightRequest="40"  
                     VerticalOptions="CenterAndExpand"  
                     HorizontalOptions="End"  
                     Margin="0,0,20,0"  
                     Clicked="BackButton_Clicked"/>  
      
                <Image   
                     Source="hfn256"  
                     Margin="2"  
                     Grid.Row="1"   
                     Grid.Column="0"  
                     Grid.ColumnSpan="3"  
                     HorizontalOptions="Center"  
                     BackgroundColor="Transparent"/>  
      
                <Label   
                     Text="Add Operator"  
                     FontSize="Large"  
                     TextColor="Black"  
                     FontAttributes="Bold"  
                     Padding =" 0, 0, 0, 0"  
                     Grid.Row="2"   
                     Grid.Column="0"  
                     Grid.ColumnSpan="3"  
                     HorizontalOptions="Center"  
                     VerticalOptions="End"  
                     BackgroundColor="Transparent"/>  
      
                <ScrollView Orientation="Vertical"  
                 Grid.Row="4" Grid.Column="0"  
                 Grid.ColumnSpan="3">  
                    <Grid BackgroundColor="White" >  
                        <Grid.RowDefinitions>  
                            <RowDefinition Height="60"/>  
                            <RowDefinition Height="50"/>  
                            <RowDefinition Height="50"/>  
                            <RowDefinition Height="50"/>  
                            <RowDefinition Height="100"/>  
                            <RowDefinition Height="50"/>  
                        </Grid.RowDefinitions>  
      
                        <Grid.ColumnDefinitions>  
                            <ColumnDefinition Width="Auto"/>  
                            <ColumnDefinition Width="80"/>  
                            <ColumnDefinition Width="*"/>  
                        </Grid.ColumnDefinitions>  
      
                        <Label  
                             x:Name="NameLabel"  
                             Text="Name: "  
                             TextColor="Black"  
                             FontSize="Medium"  
                             FontAttributes="Bold"  
                             Grid.Row="0"  
                             Margin="40, 0, 0, 0"  
                             HorizontalOptions="Start"  
                             VerticalOptions="Center"/>  
      
                        <Entry  
                             x:Name="NameEntry"  
                             Text="{Binding OperatorName}"  
                              
                             Placeholder="Enter a name / callsign"  
                             PlaceholderColor="LightGray"  
                             TextColor="DarkBlue"  
                             FontSize="Medium"  
                             Keyboard="Text"  
                             Grid.Row="0"  
                             Grid.Column="1"  
                             Grid.ColumnSpan="2"  
                             Margin="0, 0, 30, 0"/>  
      
                        <Label  
                             x:Name="PhoneLabel"  
                             Text="Phone:    +"  
                             TextColor="Black"  
                             FontSize="Medium"  
                             FontAttributes="Bold"  
                             Grid.Row="1"  
                             Grid.Column="0"  
                             Margin="40, 0, 0, 0"  
                             HorizontalOptions="Start"  
                             VerticalOptions="Center"/>  
      
                        <!--                    <Entry  
                             x:Name="PhoneCountryEntry"  
                             Placeholder="Country"  
                             PlaceholderColor="LightGray"  
                             TextColor="DarkBlue"  
                             FontSize="Medium"  
                             Keyboard="Numeric"  
                             Grid.Row="1"  
                             Grid.Column="1"  
                             Margin="0, 0, 0, 0"/> -->  
      
                        <Entry  
                             x:Name="PhoneEntry"  
                             Text="{Binding OperatorPhone}"  
                              
                             Placeholder="Phone (no spaces)"  
                             PlaceholderColor="LightGray"  
                             TextColor="DarkBlue"  
                             FontSize="Medium"  
                             Keyboard="Numeric"  
                             Grid.Row="1"  
                             Grid.Column="1"  
                             Grid.ColumnSpan="2"  
                             Margin="0, 0, 30, 0"/>  
      
                        <Label  
                             x:Name="EmailLabel"  
                             Text="Email: "  
                             TextColor="Black"  
                             FontSize="Medium"  
                             FontAttributes="Bold"  
                             Grid.Row="2"  
                             Margin="40, 0, 0, 0"  
                             HorizontalOptions="Start"  
                             VerticalOptions="Center"/>  
      
                        <Entry  
                             x:Name="EmailEntry"  
                             Text="{Binding OperatorEmail}"  
                              
                             Placeholder="Enter Email..."  
                             PlaceholderColor="LightGray"  
                             TextColor="DarkBlue"  
                             FontSize="Medium"  
                             Keyboard="Email"  
                             Grid.Row="2"  
                             Grid.Column="1"  
                             Grid.ColumnSpan="2"  
                             Margin="0, 0, 30, 0"/>  
      
                        <Label  
                             x:Name="AvatarLabel"  
                             Text="Avatar: "  
                             TextColor="Black"  
                             FontSize="Medium"  
                             FontAttributes="Bold"  
                             Grid.Row="3"  
                             Margin="40, 0, 0, 0"  
                             HorizontalOptions="Start"  
                             VerticalOptions="Center"/>  
      
                        <Entry  
                             x:Name="AvatarEntry"  
                            Text="{Binding OperatorAvatar}"  
                              
                             Placeholder="Enter Avatar URL..."  
                             PlaceholderColor="LightGray"  
                             TextColor="DarkBlue"  
                             FontSize="Medium"  
                             Keyboard="Url"  
                             Grid.Row="3"  
                             Grid.Column="1"  
                             Grid.ColumnSpan="2"  
                             Margin="0, 0, 30, 0"/>  
      
                        <Button  
                             x:Name="OKButton"  
                             Text="OK"  
                             TextColor="Black"  
                             FontAttributes="Bold"  
                             HorizontalOptions="Start"  
                             VerticalOptions="Center"  
                             WidthRequest="100"  
                             HeightRequest="40"  
                             Margin="0, 20, 0, 0"  
                             Grid.Row="4"  
                             Grid.Column="1"  
                             Clicked="OKButton_Clicked"/>  
      
                        <Button  
                             x:Name="CancelButton"  
                             Text="Cancel"  
                             TextColor="Black"  
                             FontAttributes="Bold"  
                             HorizontalOptions="End"  
                             VerticalOptions="Center"  
                             WidthRequest="100"  
                             HeightRequest="40"  
                             Margin="0, 20, 40, 0"  
                             Grid.Row="4"  
                             Grid.Column="2"  
                             Clicked="CancelButton_Clicked"/>  
      
                    </Grid>  
                </ScrollView>  
            </Grid>  
        </ContentPage.Content>  
    
    1. In OperatorsPage.xaml.cs, I changed function AddOperatorButton_Clicked to the following code. In this page, you can also use data binding for SelectedOperatorEntry Entry .
       private async void AddOperatorButton_Clicked(object sender, EventArgs e)  
       {  
           await Navigation.PushModalAsync(new NewOperatorPage {   
      
               BindingContext = new OperatorModel()  
            });  
       }  
      

    The whole code of OperatorsPage .xaml.cs

       public partial class OperatorsPage : ContentPage  
        {  
           // OperatorViewModel ovm;  
            OperatorModel opm;  
            public EventHandler<OperatorModel> ReturnValue;  
      
            public OperatorsPage(OperatorModel SelectModel)  
      
            {  
                if (Device.RuntimePlatform == Device.iOS) { Padding = new Thickness(0, 40, 0, 0); }  
      
                InitializeComponent();  
      
                //if (SelectModel != null)  
                //{  
                //    opm = SelectModel;  
                //}  
                //else  
                //{  
                //    opm = new OperatorModel();  
                //}  
      
                opm = new OperatorModel();  
                BindingContext = opm;  
            }  
      
            private async void BackButton_Clicked(object sender, EventArgs e)  
            {  
                // The if statements below added at the behest of JessieZhang-2116 (Microsoft Q&A) 13/05/2021  
                if (SelectModel != null)  
                {  
                    EventHandler<OperatorModel> handler = ReturnValue;  
                    if (handler != null)  
                    {  
                        handler(this, opm);  
                    }  
                }  
      
                await Navigation.PopModalAsync();  
                // The line above originally commented out at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
            }  
      
            private async void AddOperatorButton_Clicked(object sender, EventArgs e)  
            {  
                await Navigation.PushModalAsync(new NewOperatorPage {   
                  
                    BindingContext = new OperatorModel()  
                 });  
            }  
      
            private async void SelectOperatorButton_Clicked(object sender, EventArgs e)  
            {  
                // The lines below added at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
                SelectOperatorPage page = new SelectOperatorPage();  
                page.ReturnValue += delegate (object s, OperatorModel operatorModel)  
                {  
                    BackCall(s, operatorModel);  
                };  
      
                await Navigation.PushModalAsync(page);  
            }  
            // The line below added at the behest of LeonLu-MSFT (Microsoft Q&A) 06/05/2021  
            OperatorModel SelectModel;  
      
      
            // The lines below added at the behest of JessieZhang-2116 (Microsoft Q&A) 01/05/2021  
            private void BackCall(object s, OperatorModel model)  
            {  
                SelectModel = model;  
                opm.OperatorName = model.OperatorName;  
            }  
      
            // The lines included below in the DeleteOperatorButton_Clicked added at the behest of LeonLu-MSFT (Microsoft Q&A) 06/05/2021  
            private async void DeleteOperatorButton_Clicked(object sender, EventArgs e)  
            {  
                if (SelectModel != null)  
                {  
                    bool answer = await DisplayAlert("Warning!", "Do you really want to delete Operator " + SelectModel.OperatorName, "Yes", "No");  
      
                    if (answer == true)  
                    {  
                        OperatorViewModel.OperatorList.Remove(SelectModel);  
                        SelectModel = null;  
                        SelectedOperatorEntry.Text = "";  
                    }  
                }  
                else  
                {  
                    await DisplayAlert("Info", "Please select an Operator to delete", "OK");  
                }  
            }  
            // The lines below in UpdateOperatorButton_Clicked added at the behest of LeonLu-MSFT (Microsoft Q&A) 11/05/2021  
            private async void UpdateOperatorButton_Clicked(object sender, EventArgs e)  
            {  
      
                if (SelectModel != null)  
                {  
                    var operatorInfoPage = new OperatorInfoPage(SelectModel);  
                    operatorInfoPage.ReturnValue += delegate (object s, OperatorModel operatorModel)  
                    {  
                        BackCall(s, operatorModel);  
                    };  
                    await Navigation.PushModalAsync(operatorInfoPage);  
                }  
                else  
                {  
                    await DisplayAlert("Info", "Please select an Operator", "OK");  
                }  
            }  
        }  
    

    Note:

    I just modified some of your code which is relative to the fuction of GetOperatorsAsync and SaveItemAsync in database HFNDatabase. It works on my side.

    You can change above code according to your requirement.

    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.


0 additional answers

Sort by: Most helpful