代码之家  ›  专栏  ›  技术社区  ›  Gabe

在用户控件中显示验证错误

  •  13
  • Gabe  · 技术社区  · 15 年前

    我抛出了一个异常,但由于某些原因,当我使用标准时,控件不显示验证状态 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>
    

    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");
            }
        }
    }
    
    4 回复  |  直到 15 年前
        1
  •  8
  •   The_Black_Smurf Goutham    15 年前

    好吧,我终于想好怎么处理了。

    这里需要做的是从原始绑定复制验证并将其发送到Textbox绑定。

    要实现这一点,首先需要实现 INotifyDataErrorInfo 验证.GetErrors ).

        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
    

    下一步要做的是通知您控件它变为无效。你必须在两个地方这样做。

    第一个会在 . 第二个将在 值属性ChangedCallback函数

    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
    

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

    创建文本框绑定时,需要设置 设置为False以避免原始绑定和文本框绑定之间的通知循环。 验证Nexceptions 验证数据错误 和 必须设置为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)
    

    最后,你需要设置 和 验证说明数据错误

    <uc:MyUserControl x:Name="UC" Value="{Binding Path=Value, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True}" Height="20" Width="100" />
    
        2
  •  3
  •   surfen    15 年前

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

    1. 用户在文本框中输入文本,并且在MyUserControl_Loaded中定义的绑定将该值传递给MyUserControl.ValueProperty。
    2. 接下来,在MainPage XAML中为MyUserControl定义的绑定将值传递给模型。
    3. Model.Value.set()中引发的异常由在主页XAML中设置的绑定处理。
    4. 不会向与文本框关联的绑定转发异常。
    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
    });  
    

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

        3
  •  3
  •   Glazius    7 年前

    var valueBinding = BindingOperations.GetBindingBase(this, ValueProperty);
    if (valueBinding != null) TextBox.SetBinding(TextBox.TextProperty, valueBinding);
    
        4
  •  1
  •   Reinis    9 年前

    如果有人来找这个问题的好的解决方案(读:“不是用VBA编写的,而是完整的”),我会写一个 base class (虽然我只使用了外观不好看的控件进行了测试,但不知道它是否可以与 UserControl s) 基于C#中的@Theúu Blackúu Smurf的回答:

    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 ValidatePropertyWhenChangedCallback 作为 PropertyChangedCallback 任何要验证的依赖项属性。