UWP中如何通过字典绑定Validator的IsValid属性并规避代理属性?
问题描述
在UWP项目中开发自定义验证器绑定功能,使用多个无统一接口的自定义Validator类,希望绑定这些Validator的IsValid属性。目标是避免在ViewModel(VM)中创建只读代理属性,仅通过一个字典属性来存储和读取Validator。
现有代码结构
Validator类定义
public class Validator : ModelValidator<Model> { // ... 自定义逻辑 }
基类ModelValidator<T>
public class ModelValidator<Model> : INotifyPropertyChanged { // ... 其他实现 private bool _isValid; public bool IsValid { get { return _isValid; } set { SetField(ref _isValid, value); } } // 假设SetField会触发PropertyChanged事件 protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
当前实现(需创建代理属性)
ViewModel中需要为每个Validator创建只读代理属性:
public class VM : INotifyPropertyChanged { // ... 其他成员 public Dictionary<string, object> Validators { get; } = new Dictionary<string, object>(); public Validator Validator { get { return Validators["Validator"] as Validator; } // 可添加键检查避免异常 } // INotifyPropertyChanged实现略 }
XAML绑定:
<TextBlock Visibility="{Binding Validator.IsValid, Converter={StaticResource BooleanToVisibilityConverter}}" />
尝试过的方案及问题
方案1:直接用x:Bind访问字典
可以避免代理属性,但无法触发PropertyChanged事件刷新绑定;使用内置字典时,若Validator未提前存入会抛出异常,自定义字典虽支持后续添加但实现繁琐。
XAML代码:
<TextBlock Visibility="{x:Bind ((local:VM)DataContext).Validators['Validator'].IsValid, Converter={StaticResource BooleanToVisibilityConverter}}" />
自定义字典代码:
public class MyDictionary : Dictionary<string, IValidator> { public new IValidator this[string key] { get { return Get(key); } // ... 其他逻辑 } public IValidator Get(string key) { IValidator o; if (TryGetValue(key, out o)) { return o; } return null; } }
方案2:使用转换器绑定字典
可后续添加Validator,但绑定的是整个Validator而非仅IsValid属性,无法自动响应IsValid的变化。
XAML代码:
<TextBlock Visibility="{Binding Validators, Converter={StaticResource ValidatorSelectionConverter}, ConverterParameter=Validator}" />
需求
是否可以通过字典实现Validator的IsValid属性绑定?需要对代码进行哪些修改或补充以得到完整解决方案?
解决方案
可以通过实现支持INotifyPropertyChanged的可观察字典,结合绑定路径的动态解析来实现需求,同时解决绑定刷新和空值安全的问题。
步骤1:定义统一的Validator标记接口
为了让字典能统一存储所有Validator类型,定义一个空标记接口:
public interface IModelValidator { } // 修改基类,让所有Validator都实现该接口 public class ModelValidator<T> : INotifyPropertyChanged, IModelValidator { // 原有代码不变 }
步骤2:实现可观察字典ObservableValidatorDictionary
这个字典需要处理两种场景的通知:
- 字典中添加/移除Validator时触发绑定刷新;
- 字典内Validator的
IsValid属性变化时,通知绑定系统更新UI。
public class ObservableValidatorDictionary : Dictionary<string, IModelValidator>, INotifyPropertyChanged { public new IModelValidator this[string key] { get { TryGetValue(key, out var validator); return validator; } set { if (ContainsKey(key)) { // 移除旧Validator的属性变化监听 (this[key] as INotifyPropertyChanged)?.PropertyChanged -= Validator_PropertyChanged; } base[key] = value; // 添加新Validator的属性变化监听 (value as INotifyPropertyChanged)?.PropertyChanged += Validator_PropertyChanged; // 通知字典项变化 OnPropertyChanged($"Item[{key}]"); } } public new void Add(string key, IModelValidator value) { (value as INotifyPropertyChanged)?.PropertyChanged += Validator_PropertyChanged; base.Add(key, value); OnPropertyChanged($"Item[{key}]"); } public new bool Remove(string key) { if (TryGetValue(key, out var validator)) { (validator as INotifyPropertyChanged)?.PropertyChanged -= Validator_PropertyChanged; } var result = base.Remove(key); OnPropertyChanged($"Item[{key}]"); return result; } private void Validator_PropertyChanged(object sender, PropertyChangedEventArgs e) { // 仅当IsValid变化时通知绑定系统 if (e.PropertyName == nameof(ModelValidator<object>.IsValid)) { var key = this.FirstOrDefault(kv => kv.Value == sender).Key; if (!string.IsNullOrEmpty(key)) { OnPropertyChanged($"Item[{key}].IsValid"); } } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
步骤3:更新ViewModel的字典类型
将原有字典替换为可观察字典:
public class VM : INotifyPropertyChanged { public ObservableValidatorDictionary Validators { get; } = new ObservableValidatorDictionary(); // 其他ViewModel逻辑... // INotifyPropertyChanged实现略 }
步骤4:XAML绑定实现
直接使用绑定路径访问字典中Validator的IsValid属性,通过FallbackValue处理Validator未提前添加的异常情况:
<TextBlock Visibility="{Binding Validators[Validator].IsValid, Converter={StaticResource BooleanToVisibilityConverter}, FallbackValue=Collapsed}" />
关键说明
- 绑定路径解析:
Validators[Validator].IsValid会被UWP绑定系统正确解析,先获取字典中键为"Validator"的实例,再访问其IsValid属性; - 自动刷新:当Validator的
IsValid变化时,可观察字典会触发对应的PropertyChanged事件,通知UI更新; - 空值安全:
FallbackValue设置默认可见性,避免Validator未添加时抛出异常,同时字典索引器会返回null,绑定系统会自动处理空值。
可选优化:自定义绑定标记扩展
如果觉得绑定路径写起来繁琐,可以自定义标记扩展简化语法:
public class ValidatorBindingExtension : Binding { public ValidatorBindingExtension(string validatorKey) { Path = new PropertyPath($"Validators[{validatorKey}].IsValid"); Converter = new BooleanToVisibilityConverter(); FallbackValue = Visibility.Collapsed; } }
XAML中使用:
<TextBlock Visibility="{local:ValidatorBinding Validator}" />
内容的提问来源于stack exchange,提问作者Panthesilea
相关产品推荐
相关产品推荐

