CommunityToolkit.MVVM:ObservableValidator特性报错及导航适配咨询
问题场景
将ViewModel的[ObservableObject]特性替换为[ObservableValidator]时,触发编译器错误:
Error CS0653 Cannot apply attribute class 'ObservableValidator' because it is abstract
原可正常运行的ViewModel代码
[ObservableObject] internal sealed partial class AddSimCardViewModel : ViewModelBase { private readonly NavigationService _addSimCardViewNavigationService; [ObservableProperty] private string? _phoneNumber; public AddSimCardViewModel(NavigationService addSimCardViewNavigationService) { _addSimCardViewNavigationService = addSimCardViewNavigationService; } }
触发错误的ViewModel代码
[ObservableValidator] internal sealed partial class AddSimCardViewModel : ViewModelBase { private readonly NavigationService _addSimCardViewNavigationService; [ObservableProperty] private string? _phoneNumber; public AddSimCardViewModel(NavigationService addSimCardViewNavigationService) { _addSimCardViewNavigationService = addSimCardViewNavigationService; } }
无效的尝试写法
以下两种写法均无法运行,因为ObservableObject和ObservableValidator都是基类,不能作为接口实现:
internal sealed partial class AddSimCardViewModel : ViewModelBase, ObservableObject
internal sealed partial class AddSimCardViewModel : ViewModelBase, ObservableValidator
核心问题分析
ObservableValidator不是特性类,而是抽象基类,不能像ObservableObject那样用特性标记使用。要实现验证功能,需要让ViewModel继承ObservableValidator,但又要保留导航所需的ViewModelBase继承关系,因此需要调整类的继承层次。
解决方案
步骤1:调整ViewModelBase的继承关系
让ViewModelBase继承ObservableValidator,这样所有ViewModel子类既能拥有导航基础功能,又能实现验证能力:
internal class ViewModelBase : ObservableValidator { public virtual void Dispose() { } }
步骤2:修正ViewModel代码
移除[ObservableValidator]特性,因为ViewModel已经通过ViewModelBase间接继承了ObservableValidator,同时保留[ObservableProperty]特性来自动生成属性,还可以直接添加验证特性:
internal sealed partial class AddSimCardViewModel : ViewModelBase { private readonly NavigationService _addSimCardViewNavigationService; [ObservableProperty] [Required(ErrorMessage = "手机号不能为空")] private string? _phoneNumber; public AddSimCardViewModel(NavigationService addSimCardViewNavigationService) { _addSimCardViewNavigationService = addSimCardViewNavigationService; } }
步骤3:保持NavigationService与NavigationStore不变
原有的NavigationService和NavigationStore无需修改,因为它们依赖的ViewModelBase现在已经兼容验证功能,所有ViewModel依然是ViewModelBase的子类,导航逻辑不受影响。
原NavigationService代码:
internal sealed class NavigationService { private readonly NavigationStore _navigationStore; private readonly Func<ViewModelBase> _createViewModel; public NavigationService(NavigationStore navigationStore, Func<ViewModelBase> createViewModel) { _navigationStore = navigationStore; _createViewModel = createViewModel; } internal void Navigate() { _navigationStore.CurrentViewModel = _createViewModel(); } }
原NavigationStore代码:
internal sealed class NavigationStore { private ViewModelBase _currentViewModel; internal ViewModelBase CurrentViewModel { get => _currentViewModel; set { _currentViewModel?.Dispose(); _currentViewModel = value; OnCurrentViewModelChanged(); } } public event Action CurrentViewModelChanged; private void OnCurrentViewModelChanged() { CurrentViewModelChanged?.Invoke(); } }
备选方案(不修改ViewModelBase时)
如果不想让ViewModelBase继承ObservableValidator,可以创建两个基类分支:
- 保留原
ViewModelBase用于无需验证的ViewModel - 创建
ValidatableViewModelBase继承ViewModelBase和ObservableValidator,用于需要验证的ViewModel
然后修改NavigationService为泛型,支持不同的ViewModel基类:
internal sealed class NavigationService<TViewModel> where TViewModel : ViewModelBase { private readonly NavigationStore _navigationStore; private readonly Func<TViewModel> _createViewModel; public NavigationService(NavigationStore navigationStore, Func<TViewModel> createViewModel) { _navigationStore = navigationStore; _createViewModel = createViewModel; } internal void Navigate() { _navigationStore.CurrentViewModel = _createViewModel(); } }
对应的ValidatableViewModelBase:
internal class ValidatableViewModelBase : ViewModelBase, ObservableValidator { // 可添加通用验证逻辑 }
此时需要验证的ViewModel继承ValidatableViewModelBase,无需验证的继承原ViewModelBase,导航时使用对应的泛型NavigationService。
内容的提问来源于stack exchange,提问作者JCL

