WPF ExceptionValidationRule未显示在Validation.Errors集合中

时间:2009-06-25 18:58:17

标签: wpf validation

如果FirstName值为null或为空,我正在抛出ApplicationException,并且我试图在TextBlock中显示错误消息,该消息是ErrorTemplate的一部分。但它始终显示“异常已被抛出调用目标”。

public string FirstName
        {
            get { return _firstName;}
            set
            {
                if(String.IsNullOrEmpty(value))
                    throw new ApplicationException("FirstName cannot be null or empty!");
                _firstName = value; 

                OnPropertyChanged("FirstName");
            }
        }

<Style x:Key="TextBoxStyle" TargetType="TextBox">

            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <TextBlock DockPanel.Dock="Right"
                        Foreground="Orange"
                        FontSize="12pt"
                        Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
                            </TextBlock>
                            <Border BorderBrush="Green" BorderThickness="1">
                                <AdornedElementPlaceholder Name="MyAdorner" />
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>

        </Style>

最后这里是TextBox控件:

<TextBox Name="txtFirstName" Style="{StaticResource TextBoxStyle}" Grid.Column="1"  Grid.Row="0" Height="20" Width="100" Margin="10">
                <TextBox.Text>
                    <Binding Path="FirstName">
                        <Binding.ValidationRules>
                            <ExceptionValidationRule />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>

            </TextBox>

3 个答案:

答案 0 :(得分:5)

前几天我经历了这个问题,最后我把这个例外从房产中取出并使用了验证课。

<TextBox Name="txtFirstName" Style="{StaticResource TextBoxStyle}" Grid.Column="1"  Grid.Row="0" Height="20" Width="100" Margin="10">
        <TextBox.Text>
            <Binding Path="FirstName" >
                <Binding.ValidationRules>
                    <validators:StringRangeValidationRule 
                                    MinimumLength="1" 
                                    MaximumLength="40"
                                    ErrorMessage="Required" />
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>

从您的媒体资源中移除异常,因此它看起来像这样......

private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            _firstName = value;

            OnPropertyChanged("FirstName");
        }
    }

继承验证课程(我在某处从互联网上挖走了这个)......

public class StringRangeValidationRule : ValidationRule
{
    private int _minimumLength = -1;
    private int _maximumLength = -1;
    private string _errorMessage;

    public int MinimumLength
    {
        get { return _minimumLength; }
        set { _minimumLength = value; }
    }

    public int MaximumLength
    {
        get { return _maximumLength; }
        set { _maximumLength = value; }
    }

    public string ErrorMessage
    {
        get { return _errorMessage; }
        set { _errorMessage = value; }
    }

    public override ValidationResult Validate(object value,
        CultureInfo cultureInfo)
    {
        ValidationResult result = new ValidationResult(true, null);
        string inputString = (value ?? string.Empty).ToString();
        if (inputString.Length < this.MinimumLength ||
               (this.MaximumLength > 0 &&
                inputString.Length > this.MaximumLength))
        {
            result = new ValidationResult(false, this.ErrorMessage);
        }
        return result;
    }
}

你需要在你的xaml中添加一个命名空间引用到验证类所在的地方,称为验证器(我确定你知道这一点,但仅仅是为了完整性)

像...一样的东西。

xmlns:validators="clr-namespace:WpfApplication1"

希望这有帮助!

干杯,

安迪

答案 1 :(得分:3)

同样,本周我完成了同样的事情!我在网上找到了以下内容,将文本框包装在此...

<validators:ValidatedContent Name="Validator" >

<!-- All your textboxes here -->

</validators:ValidatedContent>

将下面的类添加到您放置另一个的相同位置...然后您可以通过单击按钮或任何您想要的地方调用Validator.Validate()。您还可以使用IsContentValid属性来决定是否要保存等。

public class ValidatedContent : Decorator
{

    #region Public Constructors

    /// <summary>
    /// Initializes a new instance of ValidatedContent
    /// </summary>
    public ValidatedContent()
    {
        ErrorMessages = new ObservableCollection<string>();

        Loaded += new RoutedEventHandler(OnValidatedContentLoaded);
    }

    #endregion

    #region Event Handlers

    /// <summary>
    /// Handles the loaded event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnValidatedContentLoaded(object sender, RoutedEventArgs e)
    {
        Queue<DependencyObject> elementQueue = new Queue<DependencyObject>();
        elementQueue.Enqueue(this.Child);

        // Iterate over all the child elements
        while (elementQueue.Count > 0)
        {
            // Dequeue the first element in the queue
            DependencyObject element = elementQueue.Dequeue();

            if (element != null)
            {
                foreach (var childElement in LogicalTreeHelper.GetChildren(element))
                {
                    if (childElement is DependencyObject)
                        elementQueue.Enqueue((DependencyObject)childElement);
                }
            }

            Control control = element as Control;

            // Mark the element as valid if it is a control
            if (control != null && GetIsRequired(element))
            {
                control.SetValue(Control.StyleProperty, RequiredControlStyle);
            }
        }

    }

    #endregion

    #region Dependency Properties

    public static readonly DependencyProperty IsContentValidProperty =
        DependencyProperty.Register("IsContentValid", typeof(bool),
        typeof(ValidatedContent), new UIPropertyMetadata(false));

    public static readonly DependencyProperty ErrorMessagesProperty =
        DependencyProperty.Register("ErrorMessages", typeof(ObservableCollection<string>),
        typeof(ValidatedContent), new UIPropertyMetadata(null));

    public static readonly DependencyProperty RequiredControlStyleProperty =
        DependencyProperty.Register("RequiredControlStyle", typeof(Style),
        typeof(ValidatedContent), new UIPropertyMetadata(null));

    public static readonly DependencyProperty IsRequiredProperty =
        DependencyProperty.RegisterAttached("IsRequired", typeof(bool),
        typeof(ValidatedContent), new UIPropertyMetadata(false));

    #endregion

    #region Public Properties

    /// <summary>
    /// Gets or sets the style to mark a required control
    /// </summary>
    public Style RequiredControlStyle
    {
        get { return (Style)GetValue(RequiredControlStyleProperty); }
        set { SetValue(RequiredControlStyleProperty, value); }
    }

    /// <summary>
    /// Gets or sets the error messages for the validated content
    /// </summary>
    public ObservableCollection<string> ErrorMessages
    {
        get { return (ObservableCollection<string>)GetValue(ErrorMessagesProperty); }
        private set { SetValue(ErrorMessagesProperty, value); }
    }

    /// <summary>
    /// Gets if the content is valid
    /// </summary>
    public bool IsContentValid
    {
        get { return (bool)GetValue(IsContentValidProperty); }
        private set { SetValue(IsContentValidProperty, value); }
    }

    #endregion

    #region Public Methods

    /// <summary>
    /// Validates the content of the decorator
    /// </summary>
    public void Validate()
    {
        IsContentValid = true;
        ErrorMessages.Clear();

        Queue<DependencyObject> elementQueue = new Queue<DependencyObject>();
        elementQueue.Enqueue(this.Child);

        // Iterate over all the child elements
        while (elementQueue.Count > 0)
        {
            // Dequeue the first element in the queue
            DependencyObject element = elementQueue.Dequeue();


            foreach (var childElement in LogicalTreeHelper.GetChildren(element))
            {
                if (childElement is DependencyObject)
                    elementQueue.Enqueue((DependencyObject)childElement);
            }


            // Validate the bindings of the element
            ValidateBindings(element);
        }
    }

    #endregion

    #region Private Methods

    /// <summary>
    /// Validates the bindings of the dependency object
    /// </summary>
    /// <param name="element"></param>
    private void ValidateBindings(DependencyObject element)
    {
        if (element != null)
        {
            Type elementType = element.GetType();

            FieldInfo[] dependencyPropertyFields = elementType.GetFields(
                BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly);

            // Iterate over all dependency properties
            foreach (FieldInfo dependencyPropertyField in dependencyPropertyFields)
            {
                DependencyProperty dependencyProperty =
                    dependencyPropertyField.GetValue(element) as DependencyProperty;

                if (dependencyProperty != null)
                {
                    Binding binding = BindingOperations.GetBinding(element, dependencyProperty);
                    BindingExpression bindingExpression = BindingOperations.GetBindingExpression(element, dependencyProperty);

                    // Issue 1822 - Extra check added to prevent null reference exceptions
                    if (binding != null && bindingExpression != null)
                    {
                        // Validate the validation rules of the binding
                        foreach (ValidationRule rule in binding.ValidationRules)
                        {
                            ValidationResult result = rule.Validate(element.GetValue(dependencyProperty),
                                CultureInfo.CurrentCulture);

                            bindingExpression.UpdateSource();

                            if (!result.IsValid)
                            {
                                ErrorMessages.Add(result.ErrorContent.ToString());
                            }

                            IsContentValid &= result.IsValid;
                        }
                    }
                }
            }
        }
    }

    #endregion

    #region Static Methods

    /// <summary>
    /// Gets the value for the IsRequired attached property
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static bool GetIsRequired(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsRequiredProperty);
    }

    /// <summary>
    /// Sets the value for the IsRequired attached property
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="value"></param>
    public static void SetIsRequired(DependencyObject obj, bool value)
    {
        obj.SetValue(IsRequiredProperty, value);
    }

    #endregion
}

答案 2 :(得分:0)

我也遇到了一些问题,所以我发布了我的解决方法,以防有人帮忙。 Text块中显示的消息不正确,因为代码中触发的Exception被包装到TargetInvocationException中。因此显示的错误消息是该异常之一,即“在调用目标处抛出了异常”。

您想要显示的异常实际上可以在TargetInvocationException的InnerException中访问,因此您可以在XAML中使用以下语句显示它消息

 Text="{Binding ElementName=MyAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent.InnerException.Message}"

我很确定必须有一种方法,ErrorContent字段中包含的错误是正确的,但我还没有挖掘到足以理解的方法。