Easily Load Appointments in .NET MAUI Scheduler with SQLite and Perform CRUD

Gayathri-github7 - Feb 8 - - Dev Community

In this blog, we’ll see how to load appointments in the .NET MAUI Scheduler with the SQLite database, perform CRUD actions (create, read, update, and delete) on the SQLite database, and dynamically update the changes in the .NET MAUI Scheduler.

The Syncfusion .NET MAUI Scheduler is a powerful tool that provides a wide range of scheduling functionalities, allowing for the efficient management of appointments.

SQLite is a lightweight, open-source, and self-contained relational database management system (RDBMS). It is popular for embedded systems, mobile apps, and desktop software due to its ease of use and efficiency. It seamlessly integrates with .NET MAUI apps to load and save data objects in shared code.

Agenda:

Let’s get started!

Note: Before proceeding, see the getting started with .NET MAUI Scheduler documentation.

Create SQLite connection, define model, and populate appointments

Let’s explore the simple steps to connect SQLite with the Syncfusion .NET MAUI Scheduler.

Step 1: Install the required package

First, we are going to install the sqlite-net-pcl package to connect the SQLite database.

Refer to the following image.

Install sqlite-net-pcl NuGet package

Step 2: Create the SQLite connection

Next, define the SQLite connection using the SQLiteConnection API and set up the database path property in the SchedulerDatabase.cs file.

readonly SQLiteConnection _database;

 public SchedulerDatabase(string dbPath)
 {
     _database = new SQLiteConnection(dbPath);
 }
Enter fullscreen mode Exit fullscreen mode

Now, create an instance for the SQLite connection with the database Path property and initialize it in the App.Xaml.cs file to use the database.

Refer to the following code example.

public static SchedulerDatabase Database
{
    get
    {
        if (database == null)
        {
            database = new SchedulerData-base(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MAUISchedulerDatabase.db3"));
        }
        return database;
    }
}

Enter fullscreen mode Exit fullscreen mode

Step 3: Define the database table model

Then, create a model class named Appointment to hold the property values from the database table in the Appointment.cs file.

public class Appointment
{
      [PrimaryKey, AutoIncrement, Unique]
      public int ID { get; set; }
      public DateTime From { get; set; }
      public DateTime To { get; set; }
      public bool AllDay { get; set; }
      public string EventName { get; set; }
      public string Notes { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Subsequently, create a table named Appointment in that SQLite database.

SchedulerDatabase.cs

_database.CreateTable<Appointment>();
Enter fullscreen mode Exit fullscreen mode

Step 4: Populate SQLite database appointments

In the SchedulerViewModel class, populate the data from the SQLite database.

public ObservableCollection<SchedulerAppointment> Appointments { get; set; }

var dataBaseAppointments = App.Database.GetSchedulerAppointment();
if (dataBaseAppointments != null && dataBaseAppointments.Count > 0)
{
    foreach (Appointment appointment in dataBaseAppointments)
    {
        Appointments.Add(new SchedulerAppointment()
        {
            StartTime = appointment.From,
            EndTime = appointment.To,
            Subject = appointment.EventName,
            IsAllDay = appointment.AllDay,
            Id = appointment.ID
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Then, retrieve the appointments from the SQLite database in the SchedulerDatabase.cs file.

public List<Appointment> GetSchedulerAppointment()
{
    return _database.Table<Appointment>().ToList();
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Bind appointments to the Scheduler

Initialize the .NET MAUI Scheduler control and bind its AppointmentsSource property to the Appointments property of the SchedulerViewModel class.

MainPage.xaml

xmlns:scheduler="clr-namespace:Syncfusion.Maui.Scheduler;assembly=Syncfusion.Maui.Scheduler"

<ContentPage.BindingContext>
    <local:SchedulerViewModel/>
</ContentPage.BindingContext>

<scheduler:SfScheduler x:Name="Scheduler"
                       View="Week"
                       Tapped="Scheduler_Tapped"
                       AppointmentsSource="{Binding Appointments}"
                       ShowWeekNumber="True"
                       AllowedViews="Day,Week,WorkWeek,Agenda,Month,TimelineDay,TimelineWeek,TimelineWorkWeek,TimelineMonth">
</scheduler:SfScheduler>
Enter fullscreen mode Exit fullscreen mode

After executing the previous code examples, we’ll get output like in the following image.

Populating appointments from SQLite database to .NET MAUI Scheduler

Populating appointments from SQLite database to .NET MAUI Scheduler

Build a schedule appointment editor

Create an appointment editor that enables the addition, saving, and deletion of appointments in the .NET MAUI Scheduler.

Refer to the following code example to create a model class to store the basic details of the appointment editor in the AppointmentEditorModel.cs file.

public class AppointmentEditorModel : INotifyPropertyChanged
{
        private string subject , notes;
        private TimeSpan startTime , endTime;
        private bool isAllDay , isEditorEnabled= true;
        private DateTime startDate, endDate;

        public string Subject
        {
            get { return subject; }
            set
            {
                subject = value;
                RaisePropertyChanged(nameof(this.Subject));
            }
        }

        public string Notes
        {
            get { return notes; }
            set
            {
                notes = value;
                RaisePropertyChanged(nameof(this.Notes));
            }
        }

        public TimeSpan StartTime
        {
            get { return startTime; }
            set
            {
                startTime = value;
                RaisePropertyChanged(nameof(this.StartTime));
            }
        }

        public TimeSpan EndTime
        {
            get { return endTime; }
            set 
            {
                endTime = value;
                RaisePropertyChanged(nameof(this.EndTime));
            }
        }

        public bool IsAllDay
        {
            get { return isAllDay; }
            set
            {
                isAllDay = value;
                RaisePropertyChanged(nameof(this.IsAllDay));
            }
        }

        public DateTime StartDate
        {
            get { return startDate; }
            set
            {
                startDate = value; 
                RaisePropertyChanged(nameof(this.StartDate));
            }
        }

        public DateTime EndDate
        {
            get { return endDate; }
            set 
            {
                endDate = value;
                RaisePropertyChanged(nameof(this.EndDate));
            }
        }

        public bool IsEditorEnabled
        {
            get { return isEditorEnabled; }
            set 
            {
                isEditorEnabled = value;
                RaisePropertyChanged(nameof(this.IsEditorEnabled));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyname)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
            }
        }
}
Enter fullscreen mode Exit fullscreen mode

Now, let’s create the appointment editor using the.NET MAUI Popup and Text Input Layout controls. Ensure that the AppointmentEditorModel property is correctly bound in each appointment editor, allowing seamless two-way value updates between the appointment and the editor.

MainPage.xaml

xmlns:inputLayout="clr-namespace:Syncfusion.Maui.Core;assembly=Syncfusion.Maui.Core"
xmlns:popup="clr-namespace:Syncfusion.Maui.Popup;assembly=Syncfusion.Maui.Popup"

<ContentPage.BindingContext>
    <local:SchedulerViewModel/>
</ContentPage.BindingContext>

<popup:SfPopup x:Name="sfPopup" IsOpen="{Binding IsOpen}" BindingContext="{Binding}" WidthRequest="550" HeightRequest="500" AutoSizeMode="Both" ShowHeader="False" ShowFooter="False">
    <popup:SfPopup.ContentTemplate>
        <DataTemplate>
            <Grid RowDefinitions="*,auto" Margin="20">
                <ScrollView>
                    <StackLayout>
                        <inputLayout:SfTextInputLayout x:Name="eventName_layout" Hint="Event name" ContainerBackground="Transparent" OutlineCornerRadius="8" ContainerType="Outlined">
                            <Entry x:Name="eventNameText" Text="{Binding AppointmentEditorModel.Subject}" />
                        </inputLayout:SfTextInputLayout>

                        <Grid ColumnDefinitions="0.5*,0.5*">
                            <inputLayout:SfTextInputLayout Hint="Start date" OutlineCornerRadius="8" ContainerBackground="Transparent" ContainerType="Outlined">
                                <DatePicker x:Name="startDate_picker" Date="{Binding AppointmentEditorModel.StartDate}" />
                            </inputLayout:SfTextInputLayout>

                            <inputLayout:SfTextInputLayout Grid.Column="1" Margin="10,0,0,0" Hint="Start time" OutlineCornerRadius="8" ContainerBackground="Transparent" ContainerType="Outlined">
                                <TimePicker x:Name="startTime_picker" IsEnabled="{Binding AppointmentEditorModel.IsEditorEnabled}" VerticalOptions="Start" Time="{Binding AppointmentEditorModel.StartTime, Mode=TwoWay}" />
                            </inputLayout:SfTextInputLayout>
                        </Grid>

                        <Grid ColumnDefinitions="0.5*,0.5*">
                            <inputLayout:SfTextInputLayout Hint="End date" OutlineCornerRadius="8" ContainerBackground="Transparent" ContainerType="Outlined">
                                <DatePicker x:Name="endDate_picker" Date="{Binding AppointmentEditorModel.EndDate}" />
                            </inputLayout:SfTextInputLayout>

                            <inputLayout:SfTextInputLayout Hint="End time" Margin="10,0,0,0" Grid.Column="1" OutlineCornerRadius="8" ContainerBackground="Transparent" ContainerType="Outlined">
                                <TimePicker x:Name="endTime_picker" IsEnabled="{Binding AppointmentEditorModel.IsEditorEnabled}" HorizontalOptions="Start" Time="{Binding AppointmentEditorModel.EndTime}" />
                            </inputLayout:SfTextInputLayout>
                        </Grid>

                        <Grid Margin="2,-12,0,0" x:Name="allDay" VerticalOptions="Start" ColumnDefinitions="50,*">
                            <Label Grid.Column="0" VerticalTextAlignment="Center" Text="AllDay"/>
                            <Switch Toggled="SwitchAllDay_Toggled" IsToggled="{Binding AppointmentEditorModel.IsAllDay}" Margin="10,0,0,0" x:Name="switchAllDay" Grid.Column="1" HorizontalOptions="Start" VerticalOptions="Center" />
                        </Grid>

                        <inputLayout:SfTextInputLayout x:Name="organizer_layout" Hint="Notes" OutlineCornerRadius="8" ContainerBackground="Transparent" ContainerType="Outlined">
                            <Editor x:Name="organizerText" Text="{Binding AppointmentEditorModel.Notes}" />
                        </inputLayout:SfTextInputLayout>
                    </StackLayout>
                </ScrollView>

                <Grid HeightRequest="40" Grid.Row="1" ColumnDefinitions="*,auto">
                    <Button x:Name="DeleteButton" Command="{Binding DeleteAppointment}" WidthRequest="80" Text="Delete" HorizontalOptions="Start" />

                    <HorizontalStackLayout HorizontalOptions="End" Grid.Column="1">
                        <Button x:Name="cancelButton" WidthRequest="80" Text="Cancel" Command="{Binding CancelEditAppointment}" />
                        <Button Margin="10,0,0,0" x:Name="saveButton" Command="{Binding AddAppointment}" Text="Save" WidthRequest="80" TextColor="White" />
                    </HorizontalStackLayout>
                </Grid>
            </Grid>
        </DataTemplate>
    </popup:SfPopup.ContentTemplate>
</popup:SfPopup>
Enter fullscreen mode Exit fullscreen mode

Refer to the following code example to initialize the AppointmentEditorModel in the SchedulerViewModel.cs file.

public AppointmentEditorModel AppointmentEditorModel { get; set; }

this.AppointmentEditorModel = new AppointmentEditorModel();

private bool isOpen;
public bool IsOpen
{
    get { return isOpen; }
    set
    {
        isOpen = value;
        OnPropertyChanged(nameof(this.IsOpen));
    }
}
Enter fullscreen mode Exit fullscreen mode

The appointment editor pop-up will be displayed upon tapping the scheduler, providing the options to add, edit, or delete the appointments. Refer to the following code examples.

MainPage.xaml.cs

private void Scheduler_Tapped(object sender, SchedulerTappedEventArgs e)
{
    if (this.BindingContext is SchedulerViewModel schedulerViewModel)
    {
        SchedulerAppointment appointment;
        DateTime selectedDate;

        if (e.Appointments != null && e.Appointments.Count > 0)
        {
            appointment = (SchedulerAppointment)e.Appointments[0];
            selectedDate = appointment.StartTime;
        }
        else
        {
            appointment = null;
            selectedDate = (DateTime)e.Date;
        }
        schedulerViewModel.UpdateEditor(appointment, selectedDate);
        sfPopup.Show();
    }
}
Enter fullscreen mode Exit fullscreen mode

SchedulerViewModel.cs

private SchedulerAppointment appointment;
private DateTime selectedDate;

internal void UpdateEditor(SchedulerAppointment appointment, DateTime selectedDate)
{
    this.appointment = appointment;
    this.selectedDate = selectedDate;

    if (this.appointment != null)
    {
        AppointmentEditorModel.Subject = this.appointment.Subject;
        AppointmentEditorModel.Notes = this.appointment.Notes;
        AppointmentEditorModel.StartDate = this.appointment.StartTime;
        AppointmentEditorModel.EndDate = this.appointment.EndTime;
        AppointmentEditorModel.IsEditorEnabled = true;

        if (!this.appointment.IsAllDay)
        {
            AppointmentEditorModel.StartTime = new TimeSpan(this.appointment.StartTime.Hour, this.appointment.StartTime.Minute, this.appointment.StartTime.Second);
            AppointmentEditorModel.EndTime = new TimeSpan(this.appointment.EndTime.Hour, this.appointment.EndTime.Minute, this.appointment.EndTime.Second);
            AppointmentEditorModel.IsAllDay = false;
            AppointmentEditorModel.IsEditorEnabled = true;
        }
        else
        {
            AppointmentEditorModel.StartTime = new TimeSpan(12, 0, 0);
            AppointmentEditorModel.EndTime = new TimeSpan(12, 0, 0);
            AppointmentEditorModel.IsEditorEnabled = false;
            AppointmentEditorModel.IsAllDay = true;
        }
    }
    else
    {
        AppointmentEditorModel.Subject = "";
        AppointmentEditorModel.Notes = "";
        AppointmentEditorModel.IsAllDay = false;
        AppointmentEditorModel.StartDate = this.selectedDate;
        AppointmentEditorModel.StartTime = new TimeSpan(this.selectedDate.Hour, this.selectedDate.Minute, this.selectedDate.Second);
        AppointmentEditorModel.EndDate = this.selectedDate;
        AppointmentEditorModel.EndTime = new TimeSpan(this.selectedDate.Hour + 1, this.selectedDate.Minute, this.selectedDate.Second);
    }
}
Enter fullscreen mode Exit fullscreen mode

After executing the previous code examples, we’ll get an output like in the following image.

Schedule appointment editor

Schedule appointment editor

Perform CRUD operations on SQLite database and update the .NET MAUI Scheduler

Let’s see how to perform CRUD actions on the SQLite database while synchronizing the changes with the .NET MAUI Scheduler control.

When you tap on the Scheduler, you can utilize the appointment editor pop-up to add, edit, and delete appointment details. On clicking the Save or Delete buttons in the appointment editor, both the Scheduler and SQLite database will be updated.

Saving an appointment

You can add appointments by providing the required details and clicking the Save button in the appointment editor. The following code examples illustrates the command associated with the Save button.

SchedulerViewModel.cs

public Command AddAppointment { get; set; }

AddAppointment = new Command(AddAppointmentDetails);

private void AddAppointmentDetails()
{
    var endDate = AppointmentEditorModel.EndDate;
    var startDate = AppointmentEditorModel.StartDate;
    var endTime = AppointmentEditorModel.EndTime;
    var startTime = AppointmentEditorModel.StartTime;

    if (endDate < startDate)
    {
        Application.Current.MainPage.DisplayAlert("", "End date should be greater than start date", "OK");
    }
    else if (endDate == startDate)
    {
        if (endTime <= startTime)
        {
            Application.Current.MainPage.DisplayAlert("", "End time should be greater than start time", "OK");
        }
        else
        {
            AppointmentDetails();
        }
    }
    else
    {
        AppointmentDetails();
    }
}

private void AppointmentDetails()
{
    if (appointment == null)
    {
        appointment = new SchedulerAppointment();
        appointment.Subject = AppointmentEditorModel.Subject;
        appointment.StartTime = AppointmentEditorModel.StartDate.Date.Add(AppointmentEditorModel.StartTime);
        appointment.EndTime = AppointmentEditorModel.EndDate.Date.Add(AppointmentEditorModel.EndTime);
        appointment.IsAllDay = AppointmentEditorModel.IsAllDay;
        appointment.Notes = AppointmentEditorModel.Notes;

        if (this.Appointments == null)
        {
            this.Appointments = new ObservableCollection<SchedulerAppointment>();
        }

        appointment.Id = Appointments.Count;
        //// Add the appointments in the Scheduler.
        Appointments.Add(appointment);
    }
    else
    {
        appointment.Subject = AppointmentEditorModel.Subject;
        appointment.StartTime = AppointmentEditorModel.StartDate.Date.Add(AppointmentEditorModel.StartTime);
        appointment.EndTime = AppointmentEditorModel.EndDate.Date.Add(AppointmentEditorModel.EndTime);
        appointment.IsAllDay = AppointmentEditorModel.IsAllDay;
        appointment.Notes = AppointmentEditorModel.Notes;
    }

    SaveSchedulerAppointmentAsync();

    this.IsOpen = false;
}
Enter fullscreen mode Exit fullscreen mode

The appointment details will be updated based on the user input, and the SaveSchedulerAppointmentAsync() method handles the addition or editing of appointments in both the Scheduler and the SQLite database.

Refer to the following code examples for saving an appointment in the SQLite database.

SchedulerViewModel.cs

private void SaveSchedulerAppointmentAsync()
{
    //// - add or edit the appointment in the database collection.
    var editAppointment = new Appointment()
    {
        From = appointment.StartTime,
        To = appointment.EndTime,
        AllDay = appointment.IsAllDay,
        Notes = appointment.Notes,
        EventName = appointment.Subject,
        ID = (int)appointment.Id
    };
    App.Database.SaveSchedulerAppointmentAsync(editAppointment);
}
Enter fullscreen mode Exit fullscreen mode

SchedulerDatabase.cs

//Insert an appointment in the database.
 public int SaveSchedulerAppointmentAsync(Appointment appointment)
 {
     if (appointment == null)
     {
         throw new Exception("Null");
     }

     return _database.InsertOrReplace(appointment);
 }
Enter fullscreen mode Exit fullscreen mode

Deleting an appointment

By clicking the Delete button, we can delete an appointment in the .NET MAUI Scheduler and in the SQLite database. The following code examples demonstrate the command associated with the Delete button.

SchedulerViewModel.cs

public Command DeleteAppointment { get; set; }

DeleteAppointment = new Command(DeleteSchedulerAppointment);

private void DeleteSchedulerAppointment()
{
    if (appointment == null)
    {
        this.IsOpen = false;
        return;
    }

    //// Remove the appointments in the Scheduler.
    Appointments.Remove(this.appointment);
    //// Delete appointment in the database.
    var deleteAppointment = new Appointment()
    {
        From = appointment.StartTime,
        To = appointment.EndTime,
        AllDay = appointment.IsAllDay,
        Notes = appointment.Notes,
        EventName = appointment.Subject,
        ID = (int)appointment.Id
    };

    App.Database.DeleteSchedulerAppointmentAsync(deleteAppointment);
    this.IsOpen = false;
}
Enter fullscreen mode Exit fullscreen mode

SchedulerDatabase.cs

//Delete an appointment in the database.
public int DeleteSchedulerAppointmentAsync(Appointment appointment)
{
      return _database.Delete(appointment);
}
Enter fullscreen mode Exit fullscreen mode

After executing the previous code examples, we’ll get output like in the following image.

Performing CRUD operations in SQLite database using .NET MAUI Scheduler

Performing CRUD operations in SQLite database using .NET MAUI Scheduler

When rerunning the app, the .NET MAUI Scheduler will fetch the updated appointments from the SQLite database, and the output will be as follows.

Updating the .NET MAUI Scheduler appointments loaded from SQLite database

Updating the .NET MAUI Scheduler appointments loaded from SQLite database

GitHub reference

For more details, refer to the complete project on the GitHub repository.

Conclusion

Thanks for reading! In this blog, we learned how to load appointments in the .NET MAUI Scheduler with SQLite and perform effortless CRUD operations. Try out the steps discussed in this blog post and leave your feedback in the comments section below!

The newest version of Essential Studio is available for existing customers on the License and Downloads page. If you’re not a Syncfusion customer, sign up for our 30-day free trial to explore our features.

For questions, you can contact us through our support forum, support portal, or feedback portal. We are always happy to assist you!

Related blogs

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .