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
相关产品推荐
相关产品推荐

