ViewModel 中的 RelayCommand 继承

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

我正在设计一个主/详细视图模型方案,因此我创建了一个基类,它实现添加、编辑、保存、取消和删除命令,并管理这些命令的

CanExecute
逻辑。但是,保存和删除命令需要在派生类中部分实现,但它们仍然必须存在于基类中以管理
CanExecute
逻辑。

总之,我尝试了这样的

RelayCommand
继承:

// Base class
   protected IRelayCommand SaveCommand { get; }
   protected void Save()
   {
      // [...]
   }

   protected IRelayCommand DeleteCommand { get; }
   protected void Delete()
   {
      // [...]
   }

// Derived class
   [RelayCommand(CanExecute = nameof(CanSave))]
   private void Save()
   {
      // [...]
      base.Save();
   }

   [RelayCommand(CanExecute = nameof(CanDelete))]
   private void Delete()
   {
      // [...]
      base.Delete();
   }

更详细地说,基类:

internal abstract partial class MasterDetailViewModelBase<TDetailViewModel> : ObservableObject
{
   // [...]

   private TDetailViewModel? currentItem;
   public TDetailViewModel? CurrentItem
   {
      get => currentItem;
      set
      {
         if (EqualityComparer<TDetailViewModel?>.Default.Equals(currentItem, value)) return;
         SetProperty(ref currentItem, value);
         OnPropertyChanged(nameof(HasCurrentItem));
         NotifyButtonsState();
      }
   }

   public bool HasCurrentItem => CurrentItem is not null;

   private void NotifyButtonsState()
   {
      OnPropertyChanged(nameof(CanAdd));
      OnPropertyChanged(nameof(CanEdit));
      OnPropertyChanged(nameof(CanSave));
      OnPropertyChanged(nameof(CanCancel));
      OnPropertyChanged(nameof(CanDelete));
      AddCommand.NotifyCanExecuteChanged();      // [NotifyCanExecuteChangedFor(nameof(AddCommand))]
      EditCommand.NotifyCanExecuteChanged();     // [NotifyCanExecuteChangedFor(nameof(EditCommand))]
      SaveCommand.NotifyCanExecuteChanged();     // [NotifyCanExecuteChangedFor(nameof(SaveCommand))]
      CancelCommand.NotifyCanExecuteChanged();   // [NotifyCanExecuteChangedFor(nameof(CancelCommand))]
      DeleteCommand.NotifyCanExecuteChanged();   // [NotifyCanExecuteChangedFor(nameof(DeleteCommand))]
   }

   public bool CanAdd => HasCurrentItem ? CurrentItem.State == ItemState.Unchanged : true;
   public bool CanEdit => HasCurrentItem ? CurrentItem.State == ItemState.Unchanged : false;
   public bool CanSave => HasCurrentItem ? CurrentItem.IsChanged : false;
   public bool CanCancel => HasCurrentItem
                                 ? CurrentItem.State == ItemState.Adding ||
                                   CurrentItem.State == ItemState.Editing
                                 : false;
   public bool CanDelete => HasCurrentItem ? CurrentItem.State == ItemState.Unchanged : false;

   [RelayCommand(CanExecute = nameof(CanAdd))]
   private void Add()
   {
      // [...]
   }

   [RelayCommand(CanExecute = nameof(CanEdit))]
   private void Edit()
   {
      // [...]
   }

   //
   // That's how CommunityToolkit.Mvvm implements the RelayCommand attribute:
   //
   //private RelayCommand? saveCommand;
   //public IRelayCommand SaveCommand => saveCommand ??= new RelayCommand(new Action(Save), () => CanSave);
   //
   protected IRelayCommand SaveCommand { get; }
   protected void Save()
   {
      // [...]
   }

   [RelayCommand(CanExecute = nameof(CanCancel))]
   private void Cancel()
   {
      // [...]
   }

   protected IRelayCommand DeleteCommand { get; }
   protected void Delete()
   {
      // [...]
   }
}

以及派生类:

internal partial class CustomersViewModel : MasterDetailViewModelBase<CustomerViewModel>
{
   [RelayCommand(CanExecute = nameof(CanSave))]
   private void Save()
   {
      // [...]

      base.Save();
   }

   [RelayCommand(CanExecute = nameof(CanDelete))]
   private void Delete()
   {
      // [...]

      base.Delete();
   }
}

但是这样做会导致通知按钮状态的函数出错,

NotifyButtonsState()
:

   private void NotifyButtonsState()
   {
      OnPropertyChanged(nameof(CanAdd));
      OnPropertyChanged(nameof(CanEdit));
      OnPropertyChanged(nameof(CanSave));
      OnPropertyChanged(nameof(CanCancel));
      OnPropertyChanged(nameof(CanDelete));
      AddCommand.NotifyCanExecuteChanged();
      EditCommand.NotifyCanExecuteChanged();
      SaveCommand.NotifyCanExecuteChanged();     // <-- ERROR
      CancelCommand.NotifyCanExecuteChanged();
      DeleteCommand.NotifyCanExecuteChanged();
   }

错误:

System.NullReferenceException:“未将对象引用设置为对象的实例。”

c# winforms mvvm community-toolkit-mvvm
1个回答
0
投票

我得出的结论是,我不能使用

RelayCommandAttribute
包中的
CommunityToolkit.Mvvm
来做到这一点,我必须自己编写代码。

因为我想给派生类留下尽可能少的工作,所以我想出了这个解决方案:

基类:

   private RelayCommand? saveCommand;
   protected IRelayCommand SaveCommand => saveCommand;
   protected IRelayCommand GetSaveCommand(Action execute)
   {
      return saveCommand ??= new RelayCommand(execute, () => CanSave);
   }
   protected void Save()
   {
      // [...]
   }

   private RelayCommand? deleteCommand;
   protected IRelayCommand DeleteCommand => deleteCommand;
   protected IRelayCommand GetDeleteCommand(Action execute)
   {
      return deleteCommand ??= new RelayCommand(execute, () => CanDelete);
   }
   protected void Delete()
   {
      // [...]
   }

派生类:

   //[RelayCommand(CanExecute = nameof(CanSave))]
   public IRelayCommand SaveCommand => base.GetSaveCommand(new Action(Save));
   private void Save()
   {
      // [...]
      base.Save();
   }

   //[RelayCommand(CanExecute = nameof(CanDelete))]
   public IRelayCommand DeleteCommand => base.GetDeleteCommand(new Action(Delete));
   private void Delete()
   {
      // [...]
      base.Delete();
   }
© www.soinside.com 2019 - 2024. All rights reserved.