简体   繁体   English

在UserControl中显示验证错误

[英]Show Validation Error in UserControl

I am not sure why the validation state doesn't get reflected in my user control. 我不确定为什么验证状态不会反映在我的用户控件中。 I am throwing an exception but for some reason the control doesn't show the validation state...When I use a standard Textbox (Which is commented out right now in my example) on my MainPage it shows the error state, not sure why its not when its wrapped. 我抛出异常,但由于某种原因,控件没有显示验证状态...当我在我的MainPage上使用标准Textbox (现在在我的示例中注释掉)时,它显示错误状态,不知道为什么它不包裹时。

I have slimmed this down so basically its a user control that wraps a TextBox . 我已经减少了这一点,所以基本上它是一个包装TextBox的用户TextBox What am I missing?? 我错过了什么?

MyUserControl XAML: MyUserControl XAML:

<UserControl x:Class="ValidationWithUserControl.MyUserControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBox x:Name="TextBox"/>
    </Grid>
</UserControl>

MyUserControl Code Behind: 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);
    }
}

My MainPage XAML: 我的主页XAML:

<Grid x:Name="LayoutRoot" Background="LightBlue">
    <StackPanel>
        <uc:MyUserControl x:Name="UC" Value="{Binding Path=Value, Mode=TwoWay}" Height="20" Width="100" />
        <!--TextBox x:Name="MS" Text="{Binding Path=Value, Mode=TwoWay, ValidatesOnExceptions=True, NotifyOnValidationError=True}" Height="20" Width="100" /-->
    </StackPanel>
</Grid>

My MainPage Code Behind: 我的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;
    }
}

My Model: 我的型号:

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");
        }
    }
}

Ok, I finally figured out how to handle this. 好吧,我终于想出了如何处理这个问题。

What you need to do here is to copy the validation from the original binding and send it to the Textbox binding. 您需要做的是从原始绑定复制验证并将其发送到Textbox绑定。

The first thing you'll need to do to achieve this is to implement the INotifyDataErrorInfo interface in your user control. 要实现这一点,您需要做的第一件事是在用户控件中实现INotifyDataErrorInfo接口。 Then you'll have to validate the user control to get the exact validation text withon the GetErrors function (This can be done with the Validation.GetErrors ). 然后,您必须验证用户控件以获取GetErrors函数的确切验证文本(可以使用Validation.GetErrors完成此操作)。

This is a basic implementation and it's in VB but I'm sure you get the point. 这是一个基本的实现,它在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

The next thing to do is to notify you control it becomes invalid. 接下来要做的就是通知你控制它变得无效。 You will have to do this in 2 places. 您必须在2个地方执行此操作。

The first one will be on the BindingValidationError event . 第一个将在BindingValidationError事件上 The second one will be in the Value PropertyChangedCallback function (It has to be specified when you register the DependencyProperty) 第二个将在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

Most of the job is done now but you still need to make some adjustments to the bindings. 大部分工作现在已完成,但您仍需要对绑定进行一些调整。

When you create the TextBox binding, you need to set the NotifyOnValidationError to False to avoid a notifications loop between the original binding and the Textbox binding. 创建TextBox绑定时,需要将NotifyOnValidationError设置为False以避免原始绑定和Textbox绑定之间的通知循环。 ValidatesOnExceptions , ValidatesOnDataErrors and ValidatesOnNotifyDataErrors need to be set to True. 需要将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)

Finaly, you need to set the NotifyOnValidationError and ValidatesOnNotifyDataErrors property to True in your XAML. 最后,您需要在XAML中将NotifyOnValidationErrorValidatesOnNotifyDataErrors属性设置为True。

<uc:MyUserControl x:Name="UC" Value="{Binding Path=Value, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}" Height="20" Width="100" />

You should be able to echo the dependency property binding directly to the usercontrol textbox. 您应该能够将依赖项属性绑定直接回显到usercontrol文本框。 This will pick up validation errors the same way bindings in the parent view would. 这将以与父视图中的绑定相同的方式获取验证错误。 In your MyUserControl_Loaded function: 在您的MyUserControl_Loaded函数中:

var valueBinding = BindingOperations.GetBindingBase(this, ValueProperty);
if (valueBinding != null) TextBox.SetBinding(TextBox.TextProperty, valueBinding);

This behaviour is caused by an added binding level. 此行为是由添加的绑定级别引起的。 Bindings do not support forwarding validation errors. 绑定不支持转发验证错误。

What happens behind the scenes: 幕后发生的事情:

  1. User inputs text into TextBox, and the binding defined in MyUserControl_Loaded passes the value to the MyUserControl.ValueProperty. 用户将文本输入到TextBox中,MyUserControl_Loaded中定义的绑定将值传递给MyUserControl.ValueProperty。
  2. Next, the binding defined in MainPage XAML for MyUserControl passes the value to the Model. 接下来,在MainPage XAML for MyUserControl中定义的绑定将值传递给Model。
  3. An Exception thrown in Model.Value.set() is handled by the binding that has been set up in MainPage XAML. Model.Value.set()中抛出的异常由MainPage XAML中设置的绑定处理。
  4. No exception is passed forward to the binding associated with a TextBox. 没有异常传递给与TextBox关联的绑定。
  5. Since UserControl does not have ValidatesOnExceptions set on true, no visual indication is displayed. 由于UserControl没有将ValidatesOnExceptions设置为true,因此不会显示任何可视指示。

To resolve this issue you could bind the text box directly to the Model like this: 要解决此问题,您可以将文本框直接绑定到模型,如下所示:

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
});  

Since 6 months have passed I wonder if and how did you overcome this problem. 从6个月过去了,我想知道你是否以及如何克服这个问题。

If somebody comes around looking for a good (read: "not written in VBA and complete") solution for this problem, I wrote a base class (although i've tested it only with lookless controls, do not know if it works with UserControl s) based on @The_Black_Smurf's answer in C#: 如果有人来寻找一个好的(阅读:“不是用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<DataErrorsChangedEventArgs> 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);
        }
    }
}

use ControlBaseWithValidation class as a base class for your lookless controls instead of the Control class and add ValidatePropertyWhenChangedCallback callback as the PropertyChangedCallback on any Dependency properties that you want to validate. 使用ControlBaseWithValidation类作为无外观控件的基类而不是Control类,并在要ValidatePropertyWhenChangedCallback任何依赖项属性上添加ValidatePropertyWhenChangedCallback回调作为PropertyChangedCallback

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM