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

.NET MAUI中未保存的编辑页对象变更意外同步至列表页问题

问题描述

我有一个.NET MAUI Shell应用,ListPage通过CollectionView展示基于SQLite存储(同步至Azure SQL数据库)的缓存对象集合。点击CollectionView中的项会弹出底部弹窗显示对象详情,弹窗的编辑按钮通过Shell.Current.GoToAsync跳转到EditPage并传递对象参数,EditPage通过ApplyQueryAttributes获取对象。

保存功能正常:点击EditPage的保存按钮时,会通过WeakReferenceMessenger通知ListPage添加/编辑对象并更新CollectionView。但存在问题:编辑对象属性(名称、日期等)后不点击保存,直接调用Shell.Current.GoToAsync("..")返回时,未保存的变更会显示在ListPage的底部弹窗中;刷新ListPage的缓存项时,这些未保存变更也会反映在CollectionView里,只有同步SQLite存储后,未保存变更才会被丢弃、恢复原始值。

解决方案

核心原因

问题出在对象引用传递:跳转到EditPage时传递的是原对象的引用,EditPage中修改属性时直接修改了ListPage缓存里的同一个实例,所以即使不保存,ListPage绑定的实例已经被修改,UI会自动更新显示变更。

解决步骤

1. 跳转EditPage时传递对象副本

不要直接传递原对象实例,而是创建一个完全独立的副本,让EditPage修改副本而非原实例:

给SurgicalActivity创建副本的方式有两种:

  • 手动复制属性:
async Task Edit(object item)
{
    if (item is SurgicalActivity originalActivity)
    {
        // 创建对象副本,复制所有需要的属性
        var activityCopy = new SurgicalActivity
        {
            Id = originalActivity.Id,
            MainProcedure = originalActivity.MainProcedure,
            Date = originalActivity.Date,
            Comments = originalActivity.Comments,
            // 复制其他所有业务属性
        };

        await Shell.Current.GoToAsync($"{nameof(ClinicalActivityEditPage)}",
            new Dictionary<string, object>
            {
                { "activity", activityCopy }
            });
    }
}
  • 实现ICloneable接口简化复制:
    先给SurgicalActivity实现接口:
public class SurgicalActivity : ICloneable
{
    // 原有属性...

    public object Clone()
    {
        return new SurgicalActivity
        {
            Id = this.Id,
            MainProcedure = this.MainProcedure,
            Date = this.Date,
            Comments = this.Comments,
            // 其他属性
        };
    }
}

然后修改Edit方法:

async Task Edit(object item)
{
    if (item is SurgicalActivity originalActivity)
    {
        var activityCopy = (SurgicalActivity)originalActivity.Clone();
        await Shell.Current.GoToAsync($"{nameof(ClinicalActivityEditPage)}",
            new Dictionary<string, object>
            {
                { "activity", activityCopy }
            });
    }
}

2. 保存时同步副本变更到原缓存实例

EditPage保存成功后,通过WeakReferenceMessenger传递修改后的副本,ListPage收到消息后,找到缓存中的原实例并更新其属性(或替换原实例):

// ListPage中注册消息的处理逻辑
WeakReferenceMessenger.Default.Register<ActivityUpdatedMessage>(this, (recipient, message) =>
{
    var updatedActivity = message.Value;
    // 从分组集合中找到原实例
    var originalActivity = GroupedActivities
        .SelectMany(group => group.Items) // 假设GroupedActivities是分组集合,Items是组内的列表
        .FirstOrDefault(a => a.Id == updatedActivity.Id);

    if (originalActivity != null)
    {
        // 覆盖原实例的属性
        originalActivity.MainProcedure = updatedActivity.MainProcedure;
        originalActivity.Date = updatedActivity.Date;
        originalActivity.Comments = updatedActivity.Comments;
        // 更新其他属性
    }

    // 通知UI刷新(如果使用ObservableCollection可省略此步)
    OnPropertyChanged(nameof(GroupedActivities));
});

3. 可选:使用ObservableCollection优化UI更新

如果GroupedActivities的组内列表是ObservableCollection<SurgicalActivity>,替换原实例后会自动触发UI更新,无需手动调用OnPropertyChanged:

// 找到原实例的索引并替换
var group = GroupedActivities.First(g => g.Items.Contains(originalActivity));
var index = group.Items.IndexOf(originalActivity);
group.Items[index] = updatedActivity;

效果说明

修改后,EditPage中编辑的是独立副本,原实例在ListPage缓存中保持原始状态,不保存返回时ListPage显示的还是原始值;只有点击保存时,才会把副本的变更同步到原实例,确保UI只显示已确认的修改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 10:52:09