sqliteentity-framework-corewinui-3community-toolkit-mvvm

Update Database Automatically When Using An Observable Collection In WinUI 3


I am trying to have my SQL Lite database automatically update whenever something is added/updated/deleted from an Observable Collection. I have the following code which manually does it inside a button function.

I wish though to do it in a way that will update the database and say if I have another tab open or another List View/Control that uses the same Observable Collection also update their stuff as well automatically.

I am using both Entity Framework Core and the MVVM Toolkit.

Xaml Page:

  <Page
    x:Class="RecipeApp.Views.TestPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:RecipeApp.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:models="using:RecipeApp.Models"
    xmlns:v="using:RecipeApp.ViewModels"
    mc:Ignorable="d"
    Background="Transparent">

    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <TextBox x:Name="deleteUserText"></TextBox>
                <Button x:Name="deleteUserBtn" Click="DeleteUserBtn_Click">Delete</Button>
            </StackPanel>

            <ListView x:Name="MyListView"
  ItemsSource="{x:Bind Users, Mode=OneWay}">
                <ListView.ItemTemplate>
                    <DataTemplate x:DataType="models:User">
                        <StackPanel>
                            <TextBlock Text="{x:Bind Id}"></TextBlock>
                            <TextBlock Text="{x:Bind UserName}"></TextBlock>
                        </StackPanel>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackPanel>
    </Grid>
</Page>

Code Behind Page:

public sealed partial class TestPage : Page
{
RecipeDBContext context;

private ObservableCollection<User> _users;

public ObservableCollection<User> Users => _users;
public TestPage()
{
    this.InitializeComponent();
    context = new RecipeDBContext();
    List<User> users = new List<User>();
    _users = new ObservableCollection<User>(context.User.ToList());
}

private void DeleteUserBtn_Click(object sender, RoutedEventArgs e)
{
    Debug.WriteLine("Total users: " + Users.Count);
    Debug.WriteLine("Text is " + deleteUserText.Text);
    if (deleteUserText.Text != String.Empty)
    {
        for (int i = Users.Count-1; i >= 0; i--)
        {
            Debug.WriteLine("I: " + i + " Username: " + Users[i].UserName);
            if (Users[i].UserName== deleteUserText.Text)
            {
                context.User.Remove(Users[i]);
                Users.RemoveAt(i);
                Debug.WriteLine("DELETED " + deleteUserText.Text);
                Debug.WriteLine("New Total users: " + Users.Count);                                
                context.SaveChanges();
            }
        }            
    }
    else
    {
        Debug.WriteLine("Textbox empty");
    }
}
}

Solution

  • For changes in the collection, number or order for example, you can use the CollectionChanged event from the ObservableCollection.

    For changes in the properties of each item in the collection, I'd use the CommunityToolkit.Mvvm NuGet package:

    public partial class User : ObservableObject
    {
        // The CommunityToolkit.Mvvm's source generator will create a `Name` property with change notification.
        [ObservableProperty]
        private string _name = string.Empty;
    
        // This is the partial method that the source generator will call when the `Name` property changes.
        partial void OnNameChanged(string? oldValue, string newValue)
        {
            // Do your logic here.
        }
    }