You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WinUI 3中使用Observable Collection自动更新SQLite数据库

需求说明

我希望在ObservableCollection中添加、更新或删除数据时自动更新SQLite数据库,目前已通过按钮点击事件实现手动更新。同时要实现:

  • 数据库自动更新
  • 使用同一ObservableCollection的其他标签页或ListView等控件自动同步更新
    当前技术栈:Entity Framework Core + MVVM Toolkit

现有代码

XAML页面

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

后台代码

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");
    }
}
}

解决方案

1. 重构为MVVM架构,将逻辑移至ViewModel

创建继承自ObservableObject的ViewModel,统一管理数据集合和数据库操作:

using Microsoft.EntityFrameworkCore;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;

namespace RecipeApp.ViewModels
{
    public partial class UserViewModel : ObservableObject
    {
        private readonly RecipeDBContext _context;

        [ObservableProperty]
        private ObservableCollection<User> _users;

        public UserViewModel(RecipeDBContext context)
        {
            _context = context;
            _ = LoadUsersAsync();
        }

        private async Task LoadUsersAsync()
        {
            var users = await _context.User.ToListAsync();
            Users = new ObservableCollection<User>(users);
        }

        [RelayCommand]
        public async Task DeleteUser(string userName)
        {
            if (string.IsNullOrEmpty(userName)) return;

            var targetUser = Users.FirstOrDefault(u => u.UserName == userName);
            if (targetUser != null)
            {
                _context.User.Remove(targetUser);
                Users.Remove(targetUser);
                await _context.SaveChangesAsync();
            }
        }

        [RelayCommand]
        public async Task AddUser(User newUser)
        {
            if (newUser == null) return;

            await _context.User.AddAsync(newUser);
            Users.Add(newUser);
            await _context.SaveChangesAsync();
        }

        [RelayCommand]
        public async Task UpdateUser(User updatedUser)
        {
            if (updatedUser == null) return;

            var existingUser = await _context.User.FindAsync(updatedUser.Id);
            if (existingUser != null)
            {
                _context.Entry(existingUser).CurrentValues.SetValues(updatedUser);
                await _context.SaveChangesAsync();
                
                // 若实体未实现INotifyPropertyChanged,替换集合项触发UI更新
                var index = Users.IndexOf(existingUser);
                if (index != -1) Users[index] = updatedUser;
            }
        }
    }
}

2. 优化实体类(支持属性更新通知)

让实体继承ObservableObject,确保属性变更时UI自动同步:

using CommunityToolkit.Mvvm.ComponentModel;

namespace RecipeApp.Models
{
    public partial class User : ObservableObject
    {
        [ObservableProperty]
        private int _id;

        [ObservableProperty]
        private string _userName;
    }
}

3. 修改XAML页面,绑定ViewModel命令和集合

<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">

    <!-- 通过依赖注入或单例共享ViewModel实例 -->
    <Page.DataContext>
        <v:UserViewModel />
    </Page.DataContext>

    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal">
                <TextBox x:Name="deleteUserText" />
                <Button Command="{x:Bind ViewModel.DeleteUserCommand}" 
                        CommandParameter="{x:Bind deleteUserText.Text}">Delete</Button>
            </StackPanel>

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

4. 共享ViewModel实例实现跨页面同步

通过依赖注入(如Microsoft.Extensions.DependencyInjection)将ViewModel注册为单例:

// 在应用启动时配置DI
services.AddDbContext<RecipeDBContext>(options => 
    options.UseSqlite("Data Source=RecipeDB.db"));
services.AddSingleton<UserViewModel>();

这样所有页面使用同一个UserViewModel实例,集合变更会自动同步到所有绑定的控件。


内容的提问来源于stack exchange,提问作者Nuvolari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:09:52