【问题标题】:Show Validation Error in UserControl在 UserControl 中显示验证错误
【发布时间】:2010-11-12 19:44:23
【问题描述】:

我不确定为什么验证状态没有反映在我的用户控件中。 我抛出异常,但由于某种原因,控件不显示验证状态...当我在 MainPage 上使用标准 Textbox(在我的示例中现在已注释掉)时,它显示错误状态,而不是确定为什么它不包装。

我已经精简了它,所以基本上它是一个包含TextBox 的用户控件。 我错过了什么??

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 背后的代码:

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:

<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>

我的主页代码背后:

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

【问题讨论】:

    标签: c# silverlight validation xaml


    【解决方案1】:

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

    您需要在这里做的是从原始绑定复制验证并将其发送到文本框绑定。

    您需要做的第一件事是在您的用户控件中实现 INotifyDataErrorInfo 接口。然后,您必须使用 GetErrors 函数验证用户控件以获取准确的验证文本(这可以通过 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 绑定之间的通知循环。 ValidatesOnExceptions、ValidatesOnDataErrors 和 ValidatesOnNotifyDataErrors 需要设置为 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 中将 NotifyOnValidationError 和 ValidatesOnNotifyDataErrors 属性设置为 True。

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

    【讨论】:

    • 这对我帮助很大。谢谢!
    • 我遇到了同样的问题,我正在按照您的回答来解决它,但它对我不起作用。我错过了什么?这是我的问题。
    • 哎呀忘了链接到我提出的问题,如果你有时间检查一下 - stackoverflow.com/questions/7349627/…
    • @SimonBesner 当您的 UserControl 中有多个控件时,您是否知道该怎么做?我想在整个 UserControl 周围显示红色边框..
    • 如果你只需要整个控件周围有一个红色边框,那么你只需要使用最后一个sn -p:
    【解决方案2】:

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

    幕后发生的事情:

    1. 用户将文本输入到 TextBox,MyUserControl_Loaded 中定义的绑定将值传递给 MyUserControl.ValueProperty。
    2. 接下来,在 MainPage XAML 中为 MyUserControl 定义的绑定将值传递给模型。
    3. Model.Value.set() 中引发的异常由 MainPage XAML 中设置的绑定处理。
    4. 没有异常被传递给与 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 个月过去了,我想知道您是否以及如何克服这个问题。

    【讨论】:

    • 这在这种特殊情况下可以工作,但您将无法为绑定在不同于“Value”的属性名称上的不同模型(或数据上下文)重用相同的用户控件。
    • 好点。将整个 TextBox 公开为公共属性怎么样?或者在 TextBox.Changed 事件上实现 INofifyPropertyChanged(去除双重绑定)
    • 我想知道如果您在用户控件中处理绑定错误事件并在那里重新抛出异常会发生什么。我稍后会试试这个。
    【解决方案3】:

    您应该能够将依赖属性绑定直接回显到用户控件文本框。这将像父视图中的绑定一样拾取验证错误。在您的 MyUserControl_Loaded 函数中:

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

    【讨论】:

    • 这对我有用,但我还需要将用户控件的 Validation ErrorTemplate 设置为 null:XAML 中的 Validation.ErrorTemplate={x:Null} 或代码隐藏中的 Validation.SetErrorTemplate(this, null);。
    • 是否可以在纯 XAML 中完成此答案中的解决方案,或者只能在代码中完成?换句话说,这个答案中代码块的 XAML 等效项是什么?
    • @user2190492 ValueProperty 只在代码隐藏中定义,所以不可能。
    【解决方案4】:

    如果有人为这个问题寻找一个好的(阅读:“不是用 VBA 编写并且完整”)解决方案,我写了一个base class(虽然我只用无外观控件测试过它,不知道是否它适用于UserControls),基于@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);
            }
        }
    }
    

    使用ControlBaseWithValidation 类作为无外观控件的基类,而不是Control 类,并在要验证的任何依赖属性上添加ValidatePropertyWhenChangedCallbackcallback 作为PropertyChangedCallback。

    【讨论】:

      猜你喜欢
      • 2013-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-26
      • 2017-08-08
      • 1970-01-01
      • 2018-12-05
      • 1970-01-01
      相关资源
      最近更新 更多