WPF中如何将ViewModel数据传入ValidationRule?我的实现为何返回Null?
我是C#和WPF编程新手,现有两个TextBox用于用户输入验证:
<TextBox x:Name="TextBox1"> <TextBox.Text> <Binding NotifyOnValidationError="True" Path="String1" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> <Binding.ValidationRules> <local:ValidationRule1 ValidatesOnTargetUpdated="True" /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> <TextBox x:Name="TextBox2"> <TextBox.Text> <Binding NotifyOnValidationError="True" Path="Int1" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> <Binding.ValidationRules> <local:ValidationRule2 ValidatesOnTargetUpdated="True" /> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
TextBox1用于输入字符串,TextBox2用于输入整数。ValidationRule1仅验证TextBox1不为空。我希望通过ValidationRule2检查两个TextBox的组合值是否已存在于数据库中,因此需要将TextBox1的字符串传递给ValidationRule2,尝试用Wrapper和BindingProxy实现后,ValidationRule2中的String1属性始终为Null,请问哪里出错了?
我的实现代码如下:
Wrapper代码
public class Wrapper : DependencyObject { public static readonly DependencyProperty NewSitzeinteilungSachnummerProperty = DependencyProperty.Register("NewSitzeinteilungSachnummer", typeof(string), typeof(Wrapper)); public string NewSitzeinteilungSachnummer { get { return (string)GetValue(NewSitzeinteilungSachnummerProperty); } set { SetValue(NewSitzeinteilungSachnummerProperty, value); } } }
BindingProxy代码
public class BindingProxy : Freezable { protected override Freezable CreateInstanceCore() { return new BindingProxy(); } public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } } public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new PropertyMetadata()); }
调整后的XAML
<TextBox x:Name="TextBox2"> <TextBox.Resources> <local2:BindingProxy x:Key="proxy" Data="{Binding}"/> </TextBox.Resources> <TextBox.Text> <Binding NotifyOnValidationError="True" Path="Int1" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> <Binding.ValidationRules> <local1:ValidationRule2 ValidatesOnTargetUpdated="True" > <local1:ValidationRule2.Wrapper> <local1:Wrapper String1="{Binding String1, Source={StaticResource proxy}}"/> </local1:ValidationRule2.Wrapper> </local1:ValidationRule2> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
1. Wrapper依赖属性与CLR属性不匹配
你定义的Wrapper依赖属性是NewSitzeinteilungSachnummerProperty,对应CLR属性是NewSitzeinteilungSachnummer,但XAML中绑定的是String1,属性名完全不对应,导致绑定失效,值始终为Null。
修正后的Wrapper代码:
public class Wrapper : DependencyObject { public static readonly DependencyProperty String1Property = DependencyProperty.Register(nameof(String1), typeof(string), typeof(Wrapper)); public string String1 { get { return (string)GetValue(String1Property); } set { SetValue(String1Property, value); } } }
2. BindingProxy的作用域与绑定路径错误
你把BindingProxy放在TextBox的Resources中,此时{Binding}的DataContext是TextBox本身,而非ViewModel,导致Proxy的Data属性无法获取到ViewModel实例。另外,绑定路径应该指向Proxy的Data.String1,而非直接String1。
修正后的BindingProxy放置位置(放在Window/UserControl的Resources中,确保DataContext是ViewModel):
<Window.Resources> <local:BindingProxy x:Key="viewModelProxy" Data="{Binding}"/> </Window.Resources>
修正后的TextBox2 XAML:
<TextBox x:Name="TextBox2"> <TextBox.Text> <Binding NotifyOnValidationError="True" Path="Int1" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True"> <Binding.ValidationRules> <local:ValidationRule2 ValidatesOnTargetUpdated="True" > <local:ValidationRule2.Wrapper> <local:Wrapper String1="{Binding Data.String1, Source={StaticResource viewModelProxy}}"/> </local:ValidationRule2.Wrapper> </local:ValidationRule2> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox>
3. ValidationRule2的Wrapper属性定义
确保ValidationRule2正确暴露Wrapper属性,以便绑定生效:
public class ValidationRule2 : ValidationRule { public Wrapper Wrapper { get; set; } public override ValidationResult Validate(object value, CultureInfo cultureInfo) { if (Wrapper == null || string.IsNullOrEmpty(Wrapper.String1)) { return new ValidationResult(false, "TextBox1的值未传入"); } string stringValue = Wrapper.String1; int? intValue = int.TryParse(value.ToString(), out int result) ? result : null; // 替换为实际数据库检查逻辑 bool isDuplicate = CheckCombinationExists(stringValue, intValue); if (isDuplicate) { return new ValidationResult(false, "该组合值已存在于数据库中"); } return ValidationResult.ValidResult; } private bool CheckCombinationExists(string str, int? num) { // 实现数据库查询逻辑 return false; } }
内容的提问来源于stack exchange,提问作者user25180321

