我已经解决了从 中提取值的问题,但BindingExpression
有一个小的限制。
首先,一些更完整的 XAML:
<Window x:Class="ValidationRuleTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ValidationRuleTest"
Title="MainWindow" Height="100" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="String 1"/>
<TextBox Grid.Column="1">
<TextBox.Text>
<Binding Path="String1" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RequiredRule ValidationStep="RawProposedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Text="String 2" Grid.Row="1"/>
<TextBox Grid.Column="1" Grid.Row="1">
<TextBox.Text>
<Binding Path="String2" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:RequiredRule ValidationStep="UpdatedValue"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</Window>
请注意,第一个 TextBox 使用ValidationStep="RawProposedValue"
(默认),而第二个使用ValidationStep="UpdatedValue"
,但两者都使用相同的验证规则。
一个简单的 ViewModel(忽略 INPC 和其他有用的东西):
class MainWindowViewModel
{
public string String1
{ get; set; }
public string String2
{ get; set; }
}
最后,新的RequiredRule:
class RequiredRule : ValidationRule
{
public override ValidationResult Validate(object value,
System.Globalization.CultureInfo cultureInfo)
{
// Get and convert the value
string stringValue = GetBoundValue(value) as string;
// Specific ValidationRule implementation...
if (String.IsNullOrWhiteSpace(stringValue))
{
return new ValidationResult(false, "Must not be empty");
}
else
{
return new ValidationResult(true, null);
}
}
private object GetBoundValue(object value)
{
if (value is BindingExpression)
{
// ValidationStep was UpdatedValue or CommittedValue (Validate after setting)
// Need to pull the value out of the BindingExpression.
BindingExpression binding = (BindingExpression)value;
// Get the bound object and name of the property
object dataItem = binding.DataItem;
string propertyName = binding.ParentBinding.Path.Path;
// Extract the value of the property.
object propertyValue = dataItem.GetType().GetProperty(propertyName).GetValue(dataItem, null);
// This is what we want.
return propertyValue;
}
else
{
// ValidationStep was RawProposedValue or ConvertedProposedValue
// The argument is already what we want!
return value;
}
}
}
如果该方法获得 BindingExpression,则该GetBoundValue()
方法将挖掘出我关心的值,或者如果不是,则简单地回退参数。真正的关键是找到“路径”,然后使用它来获取属性及其值。
限制:在我最初的问题中,我的绑定有Path="Identity.Name"
,因为我正在挖掘我的 ViewModel 的子对象。这不起作用,因为上面的代码期望路径直接指向绑定对象上的属性。幸运的是,我已经扁平化了我的 ViewModel,因此不再是这种情况,但一种解决方法可能是首先将控件的 datacontext 设置为子对象。
我想感谢 Eduardo Brites,因为他的回答和讨论让我重新开始研究这个问题,并且确实为他的难题提供了一块。此外,虽然我打算完全放弃 ValidationRules 并改用 IDataErrorInfo,但我喜欢他关于将它们一起用于不同类型和复杂性的验证的建议。