Xamarin MVVM中切换开关时如何同步更新模型与数据库?
解决方案:同步MVVM数据与数据库的即时更新
针对开关点击后需立即更新数据库,但直接在实体OnPropertyChanged中操作会导致加载阶段误执行的问题,提供以下三种可行方案:
方案一:ViewModel层监听实体属性变化
在ViewModel中持有Timer实例,订阅其PropertyChanged事件,通过标记判断实体是否完成初始化加载,仅在有效状态下执行数据库更新。
ViewModel代码示例:
public class TimerDetailViewModel : INotifyPropertyChanged { private readonly ITimerRepository _timerRepository; private Timer _timer; private bool _isLoaded; // 标记实体是否完成初始化 public Timer Timer { get => _timer; set { if (_timer != null) { _timer.PropertyChanged -= Timer_PropertyChanged; } _timer = value; if (_timer != null) { _timer.PropertyChanged += Timer_PropertyChanged; _isLoaded = true; // 实体赋值完成后标记为已加载 } } } public TimerDetailViewModel(ITimerRepository timerRepository) { _timerRepository = timerRepository; } private async void Timer_PropertyChanged(object sender, PropertyChangedEventArgs e) { // 仅处理IsOnStatus变化,且实体已完成加载 if (e.PropertyName == nameof(Timer.IsOnStatus) && _isLoaded) { await _timerRepository.AddOrUpdateTimer(Timer); MessagingCenter.Send(this, "saveoccured"); } } // 实现INotifyPropertyChanged的必要代码 public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
方案二:实体添加初始化标记,通过消息通知ViewModel处理
在Timer实体中添加初始化完成标记,仅在实体初始化完成且IsOnStatus变化时,通过消息通知ViewModel执行数据库操作,避免实体直接依赖Repository。
修改Timer实体:
public class Timer : INotifyPropertyChanged { [PrimaryKey, AutoIncrement] public int Id { get; set; } public string Name { get; set; } // 新增初始化完成标记 public bool IsInitialized { get; set; } = false; private bool isOnStatus; public bool IsOnStatus { get => isOnStatus; set { isOnStatus = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); // 仅在初始化完成且IsOnStatus变化时发送通知 if (IsInitialized && propertyName == nameof(IsOnStatus)) { MessagingCenter.Send(this, "TimerStatusChanged", this); } } }
ViewModel订阅消息处理:
public class TimerDetailViewModel { private readonly ITimerRepository _timerRepository; public Timer Timer { get; set; } public TimerDetailViewModel(ITimerRepository timerRepository) { _timerRepository = timerRepository; // 订阅实体状态变化消息 MessagingCenter.Subscribe<Timer, Timer>(this, "TimerStatusChanged", async (sender, timer) => { await _timerRepository.AddOrUpdateTimer(timer); MessagingCenter.Send(this, "saveoccured"); }); } // 加载实体后标记初始化完成 public void LoadTimer(Timer timer) { Timer = timer; Timer.IsInitialized = true; } }
方案三:绑定Switch的Toggled事件到ViewModel命令
放弃双向绑定IsToggled,直接将Switch的Toggled事件绑定到ViewModel的命令,在命令中同步更新实体状态与数据库,完全控制操作时机。
视图代码修改:
<Switch Toggled="{Binding OnStatusToggledCommand}"/>
ViewModel命令实现:
public class TimerDetailViewModel { private readonly ITimerRepository _timerRepository; public Timer Timer { get; set; } public ICommand OnStatusToggledCommand { get; set; } public TimerDetailViewModel(ITimerRepository timerRepository) { _timerRepository = timerRepository; OnStatusToggledCommand = new Command<ToggledEventArgs>(async (args) => { Timer.IsOnStatus = args.Value; await _timerRepository.AddOrUpdateTimer(Timer); MessagingCenter.Send(this, "saveoccured"); }); } }
方案选择建议
- 方案一适合需要保留双向绑定优势的场景,逻辑集中在ViewModel层,符合MVVM架构原则。
- 方案三更直接,无需处理属性变化监听,适合逻辑简单的即时操作场景。
内容的提问来源于stack exchange,提问作者Bigyan Chapagain
相关产品推荐
相关产品推荐

