这是我关于堆栈溢出的第一个问题。
我正在尝试在我的 WPF 应用程序中用 Realm DB 替换 LiteDB。
我的目标是将 ListBox 和 DataGrid 绑定到视图模型中的实时查询,以便 UI 元素在数据库操作后反映数据库状态。
当我将对象添加到 Realm 数据库时,我的应用程序的行为符合预期,并且列表控件按预期更新,因为
RealmResults
应该是可观察的。但是,当我尝试从数据库中删除对象时,列表控件不会自动刷新,并且会引发异常。
我将示例应用程序调整为 WinUI 3,删除操作的行为符合预期(即 UI 列表框项目相应删除)。我当时认为 WPF 与 Realm-dotnet 不能很好地配合。
每次从数据库中删除对象时,除了重新分配 ItemsSource 之外,我还能做些什么吗?
这是一个演示该问题的示例(我的真实应用程序很大,无法粘贴到此处)
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MongoDB.Bson;
using Realms;
using Realms.Exceptions;
namespace RealmDb
{
public partial class MainWindowViewModel : ObservableObject
{
public MainWindowViewModel()
{
var config = new RealmConfiguration(@"d:\temp\cats.realm");
try
{
Realm.DeleteRealm(config);
}
catch(Exception ex){ }
LocalRealm = Realm.GetInstance(config);
Cats = LocalRealm.All<Cat>();
}
public Realm LocalRealm;
private IQueryable<Cat> _cats;
public IQueryable<Cat> Cats
{
get => _cats;
set => SetProperty(ref _cats, value);
}
public Cat? SelectedCat { get; set; }
[RelayCommand]
private void RemoveCat()
{
if (SelectedCat == null)
return;
try
{
// throws exception: Attempting to access an invalid object
LocalRealm.Write(() =>
{
LocalRealm.Remove(SelectedCat);
});
/*
Workaround:
Cats = null;
Cats = LocalRealm.All<Cat>();*/
}
catch (Exception ex)
{
// the selected cat is not removed from the datagrid.
// In the case of binding to a listbox. The deleted object still shows but it rises exception if selected.
MessageBox.Show($@"Error deleting the selected cat. {ex.Message}");
}
}
[RelayCommand]
private void Populate()
{
Cat ninja = new() { Name = "Ninja", Age = 1, Breed = "Angora" };
Cat nounou = new() { Name = "Nounou", Age = 2, Breed = "Siamese" };
Cat leila = new() { Name = "Layla", Age = 3, Breed = "Local" };
try
{
// grouped writes trows "Range actions are not supported"
LocalRealm.Write(() =>
{
LocalRealm.Add(nounou);
});
LocalRealm.Write(() =>
{
LocalRealm.Add(ninja);
});
LocalRealm.Write(() =>
{
LocalRealm.Add(leila);
});
}
catch (RealmFileAccessErrorException ex)
{
MessageBox.Show($@"Error writeing to the realm file. {ex.Message}");
}
}
}
}
MainWindow.xaml
<Window x:Class="RealmDb.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RealmDb"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
DataContext="{Binding Source={x:Static local:App.MainViewModel}}">
<StackPanel>
<Button Content="Populate" Command="{Binding PopulateCommand}" />
<Button Content="Remove" Command="{Binding RemoveCatCommand}" />
<DataGrid Name="dataGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Cats, Mode=TwoWay}"
SelectedItem="{Binding SelectedCat, Mode=TwoWay}" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
<DataGridTextColumn Header="Breed" Binding="{Binding Breed}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Window>
Cat.cs
public partial class Cat : IRealmObject
{
[PrimaryKey]
public ObjectId Id { get; set; } = ObjectId.GenerateNewId();
public string Name { get; set; } = string.Empty;
public int Age { get; set; } = 0;
public string Breed { get; set; } = string.Empty;
}
App.xaml.cs
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace RealmDb
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private static MainWindowViewModel? _mainWindowsViewModel;
public static MainWindowViewModel MainViewModel => _mainWindowsViewModel ??= new MainWindowViewModel();
}
}
不幸的是,WPF 应用程序不支持双向自动数据绑定,就像 MAUI/Xamarin.Forms 应用程序一样。这意味着 UI 中的更改不会自动创建事务来将更新保留在数据库中 - 您要么需要显式启动写入事务,要么需要创建一些自定义行为来启动写入事务(如果有)尚不存在,请执行属性更改,然后在必要时提交事务。
来自 Realm .NET 的这个片段大致就是这样做的。它与 Xamarin.Forms/MAUI 数据绑定引擎集成,并替换绑定引擎用于创建这些隐式事务的 RealmObject 属性设置器。