基于 WPF 列表的控件支持双向绑定到 Realm-dotnet 实时查询吗?

问题描述 投票:0回答:1

这是我关于堆栈溢出的第一个问题。

我正在尝试在我的 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();
    
        }
    }
c# wpf realm
1个回答
0
投票

不幸的是,WPF 应用程序不支持双向自动数据绑定,就像 MAUI/Xamarin.Forms 应用程序一样。这意味着 UI 中的更改不会自动创建事务来将更新保留在数据库中 - 您要么需要显式启动写入事务,要么需要创建一些自定义行为来启动写入事务(如果有)尚不存在,请执行属性更改,然后在必要时提交事务。

来自 Realm .NET 的

这个片段大致就是这样做的。它与 Xamarin.Forms/MAUI 数据绑定引擎集成,并替换绑定引擎用于创建这些隐式事务的 RealmObject 属性设置器。

© www.soinside.com 2019 - 2024. All rights reserved.