非常感谢弗雷德里克·赫布拉德的解决方案。它也帮助了我。我也同意卢克·科滕的观点,认为它最好作为一种行为。这样就不会在视图层中混合应用程序逻辑,视图模型也不必担心只需简单地在那里进行双重应用验证。以下是我的行为学版本:
如Fredrik Hedblad所述,首先确保任何控件验证都具有binding属性NotifyOnValidationError=“True”。
这是视图逻辑。。。简单得多。。。
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
然后就在窗口的开始标签下面
Height="Auto" Width="Auto">
<i:Interaction.Behaviors>
<behavior:ValidationErrorMappingBehavior HasValidationError="{Binding IsInvalid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</i:Interaction.Behaviors
然后对于按钮,像普通的那样绑定命令。我们将使用基本的视图模型绑定原则来使用RelayCommand禁用它。
<Button x:Name="OKButton" Content="OK" Padding="5,0" MinWidth="70" Height="23"
HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="5,5,0,0"
Command="{Binding OKCommand}"/>
现在,视图模型及其基本属性和命令
private bool _isInvalid = false;
public bool IsInvalid
{
get { return _isInvalid; }
set { SetProperty<bool>(value, ref _isInvalid); }
}
private ICommand _okCommand;
public ICommand OKCommand
{
get
{
if (_okCommand == null)
{
_okCommand = new RelayCommand(param => OnOK(), canparam => CanOK());
}
return _okCommand;
}
}
private void OnOK()
{
// this.IsInvalid = false, so we're good... let's just close
OnCloseRequested();
}
private bool CanOK()
{
return !this.IsInvalid;
}
现在,行为
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace UI.Behavior
{
public class ValidationErrorMappingBehavior : Behavior<Window>
{
#region Properties
public static readonly DependencyProperty ValidationErrorsProperty = DependencyProperty.Register("ValidationErrors", typeof(ObservableCollection<ValidationError>), typeof(ValidationErrorMappingBehavior), new PropertyMetadata(new ObservableCollection<ValidationError>()));
public ObservableCollection<ValidationError> ValidationErrors
{
get { return (ObservableCollection<ValidationError>)this.GetValue(ValidationErrorsProperty); }
set { this.SetValue(ValidationErrorsProperty, value); }
}
public static readonly DependencyProperty HasValidationErrorProperty = DependencyProperty.Register("HasValidationError", typeof(bool), typeof(ValidationErrorMappingBehavior), new PropertyMetadata(false));
public bool HasValidationError
{
get { return (bool)this.GetValue(HasValidationErrorProperty); }
set { this.SetValue(HasValidationErrorProperty, value); }
}
#endregion
#region Constructors
public ValidationErrorMappingBehavior()
: base()
{ }
#endregion
#region Events & Event Methods
private void Validation_Error(object sender, ValidationErrorEventArgs e)
{
if (e.Action == ValidationErrorEventAction.Added)
{
this.ValidationErrors.Add(e.Error);
}
else
{
this.ValidationErrors.Remove(e.Error);
}
this.HasValidationError = this.ValidationErrors.Count > 0;
}
#endregion
#region Support Methods
protected override void OnAttached()
{
base.OnAttached();
Validation.AddErrorHandler(this.AssociatedObject, Validation_Error);
}
protected override void OnDetaching()
{
base.OnDetaching();
Validation.RemoveErrorHandler(this.AssociatedObject, Validation_Error);
}
#endregion
}
}