You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WPF MVVM Caliburn Micro:数据保存后自动刷新DataGrid实现

解决Caliburn.MVVM项目中保存数据后自动刷新DataGrid的问题

嘿,我之前用Caliburn.Micro重构过类似的WinForms转WPF的项目,刚好碰到过这个保存后刷新DataGrid的问题,给你几个实用的解决方案:

1. 用ObservableCollection绑定DataGrid(基础核心)

首先,你的ViewModel里用来绑定DataGrid的集合必须用ObservableCollection,而不是普通的List。因为ObservableCollection自带集合变更通知,当你添加、删除或者替换整个集合时,UI会自动感知并刷新。

结合Caliburn.Micro的PropertyChangedBase(ViewModel建议继承这个,或者Screen),代码可以这么写:

using Caliburn.Micro;
using System.Collections.ObjectModel;

public class MainViewModel : PropertyChangedBase
{
    private readonly IGameRepository _gameRepository; // 假设你有数据仓储类
    private ObservableCollection<GameModel> _games;

    public ObservableCollection<GameModel> Games
    {
        get => _games;
        set => Set(ref _games, value); // Caliburn的Set方法自动触发PropertyChanged事件
    }

    public MainViewModel(IGameRepository gameRepository)
    {
        _gameRepository = gameRepository;
        LoadGames(); // 初始化加载数据
    }

    private async void LoadGames()
    {
        var games = await _gameRepository.GetAllAsync();
        Games = new ObservableCollection<GameModel>(games);
    }
}

2. 保存后直接更新集合(最简单的同步方式)

当你完成数据库保存操作后,只需要重新从数据库拉取最新数据,然后替换掉ViewModel里的Games集合即可——因为我们用了Set方法,它会自动触发UI更新。

示例保存方法:

public async Task SaveGame(GameModel gameToSave)
{
    // 执行数据库保存逻辑
    await _gameRepository.SaveOrUpdateAsync(gameToSave);
    
    // 重新加载最新数据并更新集合
    var latestGames = await _gameRepository.GetAllAsync();
    Games = new ObservableCollection<GameModel>(latestGames);
}

3. 确保GameModel实现属性变更通知(针对单条数据修改)

如果你的场景是修改DataGrid里的某一行数据(比如直接在DataGrid里编辑GameName),想要实时刷新该行的显示,那你的GameModel也需要实现INotifyPropertyChanged。最方便的是直接继承Caliburn.Micro的PropertyChangedBase

using Caliburn.Micro;

public class GameModel : PropertyChangedBase
{
    private int _gameID;
    public int GameID
    {
        get => _gameID;
        set => Set(ref _gameID, value);
    }

    private string _gameName;
    public string GameName
    {
        get => _gameName;
        set => Set(ref _gameName, value);
    }

    private string _gameCategory;
    public string GameCategory
    {
        get => _gameCategory;
        set => Set(ref _gameCategory, value);
    }

    private string _gameCompany;
    public string GameCompany
    {
        get => _gameCompany;
        set => Set(ref _gameCompany, value);
    }

    private decimal _gamePrice;
    public decimal GamePrice
    {
        get => _gamePrice;
        set => Set(ref _gamePrice, value);
    }
}

4. 用EventAggregator实现跨ViewModel刷新(解耦场景)

如果你的保存操作是在另一个ViewModel里(比如弹出的编辑窗口),不想直接引用主ViewModel,那可以用Caliburn.Micro自带的EventAggregator来解耦:

第一步:定义事件类

public class GameSavedEvent
{
    // 可以按需添加属性,比如区分是新增还是修改
    public bool IsNewGame { get; set; }
}

第二步:在保存ViewModel里发布事件

using Caliburn.Micro;

public class EditGameViewModel : Screen
{
    private readonly IEventAggregator _eventAggregator;
    private readonly IGameRepository _gameRepository;
    public GameModel CurrentGame { get; set; }

    public EditGameViewModel(IEventAggregator eventAggregator, IGameRepository gameRepository)
    {
        _eventAggregator = eventAggregator;
        _gameRepository = gameRepository;
    }

    public async Task Save()
    {
        await _gameRepository.SaveOrUpdateAsync(CurrentGame);
        // 发布保存完成事件到UI线程
        await _eventAggregator.PublishOnUIThreadAsync(new GameSavedEvent { IsNewGame = CurrentGame.GameID == 0 });
        // 关闭窗口
        await TryCloseAsync(true);
    }
}

第三步:在主ViewModel里订阅事件并刷新

using Caliburn.Micro;
using System;

public class MainViewModel : PropertyChangedBase, IHandle<GameSavedEvent>
{
    private readonly IEventAggregator _eventAggregator;
    private readonly IGameRepository _gameRepository;
    private IDisposable _eventSubscription;

    public ObservableCollection<GameModel> Games { get; set; }

    public MainViewModel(IEventAggregator eventAggregator, IGameRepository gameRepository)
    {
        _eventAggregator = eventAggregator;
        _gameRepository = gameRepository;
        // 订阅事件(指定UI线程处理)
        _eventSubscription = _eventAggregator.SubscribeOnUIThread(this);
        LoadGames();
    }

    private async void LoadGames()
    {
        var games = await _gameRepository.GetAllAsync();
        Games = new ObservableCollection<GameModel>(games);
    }

    // 处理保存完成事件的方法,Caliburn会自动匹配
    public async Task HandleAsync(GameSavedEvent message)
    {
        // 重新加载数据刷新DataGrid
        var latestGames = await _gameRepository.GetAllAsync();
        Games = new ObservableCollection<GameModel>(latestGames);
    }

    // 记得在ViewModel销毁时取消订阅,避免内存泄漏
    protected override void OnDeactivate(bool close)
    {
        _eventSubscription?.Dispose();
        base.OnDeactivate(close);
    }
}

这些方案里,前两种是最常用的,简单直接;如果涉及多ViewModel交互,用EventAggregator会更优雅。根据你的实际场景选就行~

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

火山引擎 最新活动