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

WPF列表控件绑定Realm-dotnet实时查询删除异常求助

WPF中Realm DB删除对象后UI不自动刷新的问题解决办法

我正尝试在WPF应用中将LiteDB替换为Realm DB,目标是让ListBox和DataGrid绑定到视图模型的实时查询,确保数据库操作后UI能同步更新状态。

添加对象时UI能正常更新,因为RealmResults是可观察的,但删除对象时,列表控件不会自动刷新,还会抛出**"尝试访问无效对象"**的异常。把示例适配到WinUI 3后删除操作正常,推测是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
            {
                // 抛出异常:尝试访问无效对象
                LocalRealm.Write(() =>
                {
                    LocalRealm.Remove(SelectedCat);

                });

                /*
                 临时解决办法: 
                Cats = null;
                Cats = LocalRealm.All<Cat>();*/
            }
            catch (Exception ex)
            {
                // 选中的猫未从DataGrid中移除。
                // 若绑定到ListBox,已删除对象仍会显示,但选中时会抛出异常。
                MessageBox.Show($@"删除选中猫时出错。{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
            {
                // 批量写入抛出"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($@"写入Realm文件时出错。{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>
    /// App.xaml的交互逻辑
    /// </summary>
    public partial class App : Application
    {
        private static MainWindowViewModel? _mainWindowsViewModel;
        public static MainWindowViewModel MainViewModel => _mainWindowsViewModel ??= new MainWindowViewModel();
    }
}

问题分析与解决办法

核心原因

WPF的ItemsControl对IQueryable<T>的集合变更通知支持不完善,而Realm默认返回的IQueryable<Cat>无法触发WPF需要的INotifyCollectionChanged事件。另外,删除对象后SelectedCat仍指向已被Realm标记为无效的对象,导致绑定访问时抛出异常。

具体解决方案

1. 使用Realm专用的可观察集合

Realm提供了RealmCollection<T>,它原生实现了INotifyCollectionChanged,WPF可以正确识别并自动刷新UI。修改ViewModel中的属性:

// 替换原有的IQueryable<Cat>类型
private RealmCollection<Cat> _cats;
public RealmCollection<Cat> Cats
{
    get => _cats;
    set => SetProperty(ref _cats, value);
}

// 构造函数中初始化
Cats = LocalRealm.All<Cat>().AsRealmCollection();

2. 删除后清空选中项

在删除操作完成后,立即清空SelectedCat,避免UI继续绑定已失效的对象:

[RelayCommand]
private void RemoveCat()
{
    if (SelectedCat == null)
        return; 

    try
    {
        LocalRealm.Write(() =>
        {
            LocalRealm.Remove(SelectedCat);
        });
        // 清空选中项,消除无效对象访问异常
        SelectedCat = null;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"删除选中猫时出错。{ex.Message}");
    }
}

3. 优化批量写入

不需要分多次事务添加对象,同一个Write事务中可以完成多个对象的添加:

[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
    {
        LocalRealm.Write(() =>
        {
            LocalRealm.Add(nounou);
            LocalRealm.Add(ninja);
            LocalRealm.Add(leila);
        });
    }
    catch (RealmFileAccessErrorException ex)
    {
        MessageBox.Show($"写入Realm文件时出错。{ex.Message}");
    }
}

修改后的完整ViewModel代码

using System;
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>().AsRealmCollection();
        }

        public Realm LocalRealm;

        private RealmCollection<Cat> _cats;
        public RealmCollection<Cat> Cats
        {
            get => _cats;
            set => SetProperty(ref _cats, value);
        }
   
        [ObservableProperty]
        private Cat? _selectedCat;

        [RelayCommand]
        private void RemoveCat()
        {
            if (SelectedCat == null)
                return; 
    
            try
            {
                LocalRealm.Write(() =>
                {
                    LocalRealm.Remove(SelectedCat);
                });
                SelectedCat = null;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"删除选中猫时出错。{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
            {
                LocalRealm.Write(() =>
                {
                    LocalRealm.Add(nounou);
                    LocalRealm.Add(ninja);
                    LocalRealm.Add(leila);
                });
            }
            catch (RealmFileAccessErrorException ex)
            {
                MessageBox.Show($"写入Realm文件时出错。{ex.Message}");
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 10:36:01