在UserControl中显示validation错误

我不确定为什么validation状态不会反映在我的用户控件中。 我抛出exception,但由于某种原因,控件没有显示validation状态…当我在我的MainPage上使用标准Textbox (现在在我的示例中注释掉)时,它显示错误状态,不知道为什么它不包裹的时候。

我已经减少了这一点,所以基本上它是一个包装TextBox的用户TextBox 。 我错过了什么?

MyUserControl XAML:

      

MyUserControl代码背后:

 public partial class MyUserControl : UserControl { public MyUserControl() { InitializeComponent(); this.Loaded += new RoutedEventHandler(MyUserControl_Loaded); this.TextBox.Unloaded += new RoutedEventHandler(TextBox_Unloaded); } public string Value { get { return (string)base.GetValue(ValueProperty); } set { base.SetValue(ValueProperty, value); } } public static DependencyProperty ValueProperty = DependencyProperty.Register( "Value", typeof(string), typeof(MyUserControl), new PropertyMetadata(null)); private void MyUserControl_Loaded(object sender, RoutedEventArgs e) { this.TextBox.SetBinding(TextBox.TextProperty, new Binding() { Source = this, Path = new PropertyPath("Value"), Mode = BindingMode.TwoWay, ValidatesOnExceptions = true, NotifyOnValidationError= true }); } private void TextBox_Unloaded(object sender, RoutedEventArgs e) { this.TextBox.ClearValue(TextBox.TextProperty); } } 

我的主页XAML:

       

我的MainPage代码背后:

 public partial class MainPage : UserControl { private Model model; //private Model model2; public MainPage() { InitializeComponent(); this.model = new Model("UC"); //this.model2 = new Model("MS"); this.UC.DataContext = this.model; //this.MS.DataContext = this.model2; } } 

我的型号:

 public class Model { public Model(string answer) { this.answer = answer; } private string answer; public string Value { get { return this.answer; } set { if (!String.IsNullOrEmpty(value)) this.answer = value; else throw new Exception("Error"); } } } 

好的,我终于想出了如何处理这个问题。

您需要做的是从原始绑定复制validation并将其发送到Textbox绑定。

要实现这一点,您需要做的第一件事是在用户控件中实现INotifyDataErrorInfo接口。 然后,您必须validation用户控件以获取GetErrors函数中的确切validation文本(可以使用Validation.GetErrors完成此操作)。

这是一个基本的实现,它在VB中,但我相信你明白了。

  Public Event ErrorsChanged(ByVal sender As Object, ByVal e As System.ComponentModel.DataErrorsChangedEventArgs) Implements System.ComponentModel.INotifyDataErrorInfo.ErrorsChanged Public Function GetErrors(ByVal propertyName As String) As System.Collections.IEnumerable Implements System.ComponentModel.INotifyDataErrorInfo.GetErrors Dim returnValue As System.Collections.IEnumerable = Nothing Dim errorMessage As String = Nothing If propertyName = "Value" Then If Validation.GetErrors(Me).Count = 0 Then errorMessage = "" Else errorMessage = Validation.GetErrors(Me).First.ErrorContent.ToString End If If String.IsNullOrEmpty(errorMessage) Then returnValue = Nothing Else returnValue = New List(Of String)() From {errorMessage} End If End If Return returnValue End Function Public ReadOnly Property HasErrors As Boolean Implements System.ComponentModel.INotifyDataErrorInfo.HasErrors Get Return Validation.GetErrors(Me).Any() End Get End Property 

接下来要做的就是通知你控制它变得无效。 您必须在2个地方执行此操作。

第一个将在BindingValidationError事件上 。 第二个将在Value PropertyChangedCallback函数中 (必须在注册DependencyProperty时指定)

 Public Shared ValueProperty As DependencyProperty = DependencyProperty.Register("Value", GetType(String), GetType(XDateTimePicker), New PropertyMetadata(Nothing, AddressOf ValuePropertyChangedCallback)) Public Shared Sub ValuePropertyChangedCallback(ByVal dependencyObject As DependencyObject, ByVal dependencyPropertyChangedEventArgs As DependencyPropertyChangedEventArgs) DirectCast(dependencyObject, MyUserControl).NotifyErrorsChanged("Value") End Sub Private Sub MyUserControl_BindingValidationError(ByVal sender As Object, ByVal e As System.Windows.Controls.ValidationErrorEventArgs) Handles Me.BindingValidationError Me.NotifyErrorsChanged("Value") End Sub Public Sub NotifyErrorsChanged(ByVal propertyName As String) RaiseEvent ErrorsChanged(Me, New System.ComponentModel.DataErrorsChangedEventArgs(propertyName)) End Sub 

大部分工作现在已完成,但您仍需要对绑定进行一些调整。

创建TextBox绑定时,需要将NotifyOnValidationError设置为False以避免原始绑定和Textbox绑定之间的通知循环。 需要将ValidatesOnExceptionsValidatesOnDataErrorsValidatesOnNotifyDataErrors设置为True。

  Dim binding As New System.Windows.Data.Binding binding.Source = Me binding.Path = New System.Windows.PropertyPath("Value") binding.Mode = Data.BindingMode.TwoWay binding.NotifyOnValidationError = False binding.ValidatesOnExceptions = True binding.ValidatesOnDataErrors = True binding.ValidatesOnNotifyDataErrors = True Me.TextBox1.SetBinding(TextBox.TextProperty, binding) 

最后,您需要在XAML中将NotifyOnValidationErrorValidatesOnNotifyDataErrors属性设置为True。

  

此行为是由添加的绑定级别引起的。 绑定不支持转发validation错误。

幕后发生的事情:

  1. 用户将文本输入到TextBox中,MyUserControl_Loaded中定义的绑定将值传递给MyUserControl.ValueProperty。
  2. 接下来,在MainPage XAML for MyUserControl中定义的绑定将值传递给Model。
  3. Model.Value.set()中抛出的exception由MainPage XAML中设置的绑定处理。
  4. 没有exception传递给与TextBox关联的绑定。
  5. 由于UserControl没有将ValidatesOnExceptions设置为true,因此不会显示任何可视指示。

要解决此问题,您可以将文本框直接绑定到模型,如下所示:

 this.TextBox.SetBinding(TextBox.TextProperty, new Binding() { Source = this.DataContext, // bind to the originating source Path = new PropertyPath("Value"), Mode = BindingMode.TwoWay, ValidatesOnExceptions = true, NotifyOnValidationError= true }); 

从6个月过去了,我想知道你是否以及如何克服这个问题。

如果有人来寻找一个好的(阅读:“不是用VBA编写并完成”)这个问题的解决方案,我写了一个基类 (虽然我只用无形控件测试它,不知道它是否适用于UserControl s)基于@The_Black_Smurf在C#中的回答:

 namespace MyApplication.Controls { using System; using System.Collections; using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; public abstract class ControlBaseWithValidation : Control, INotifyDataErrorInfo { public ControlBaseWithValidation() { // remove the red border that wraps the whole control by default Validation.SetErrorTemplate(this, null); } public delegate void ErrorsChangedEventHandler(object sender, DataErrorsChangedEventArgs e); public event EventHandler ErrorsChanged; public bool HasErrors { get { var validationErrors = Validation.GetErrors(this); return validationErrors.Any(); } } public IEnumerable GetErrors(string propertyName) { var validationErrors = Validation.GetErrors(this); var specificValidationErrors = validationErrors.Where( error => ((BindingExpression)error.BindingInError).TargetProperty.Name == propertyName).ToList(); var specificValidationErrorMessages = specificValidationErrors.Select(valError => valError.ErrorContent); return specificValidationErrorMessages; } public void NotifyErrorsChanged(string propertyName) { if (ErrorsChanged != null) { ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName)); } } protected static void ValidatePropertyWhenChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) { ((ControlBaseWithValidation)dependencyObject).NotifyErrorsChanged(dependencyPropertyChangedEventArgs.Property.Name); } } } 

使用ControlBaseWithValidation类作为无外观控件的基类而不是Control类,并在要ValidatePropertyWhenChangedCallback任何依赖项属性上将ValidatePropertyWhenChangedCallback回调添加为PropertyChangedCallback