我正在设计一个主/详细视图模型方案,因此我创建了一个基类,它实现添加、编辑、保存、取消和删除命令,并管理这些命令的
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:“未将对象引用设置为对象的实例。”
我得出的结论是,我不能使用
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();
}