tags:

views:

667

answers:

2

I am throwing an ApplicationException if the FirstName value is null or empty and I am trying to display the error message in the TextBlock which is part of the ErrorTemplate. But it always shows "Exception has been thrown at the target of invocation".

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>

And finally here is the TextBox control:

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

Hi,

I went through this the other day, I ended up taking the exception off the property and using a validation class.

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

Remove the exception from your property so it just looks like this ...

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

            OnPropertyChanged("FirstName");
        }
    }

Heres the validation class (I poached this off the internet somewhere) ...

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

You'll need to add a namespace reference in your xaml to where the validation class lives called validators (i'm sure you know this but just for completeness)

Something like ...

xmlns:validators="clr-namespace:WpfApplication1"

Hope this helps!

Cheers,

Andy

Andy Clarke
Thanks that did work out. One more question! The validation only fires if I type something in the TextBox and then remove it and then lost focus from the TextBox. Is there anyway to fire the validation when the TextBox is empty and lost focus.
azamsharp
+2  A: 

Again, I went through exactly the same thing this week! I found the following on the web, wrap your text boxes in this ...

<validators:ValidatedContent Name="Validator" >

<!-- All your textboxes here -->

</validators:ValidatedContent>

Add the class below in the same place you put the other one ... and then you can call Validator.Validate() from a button click or where ever you want. There is also an IsContentValid property you can use to decide whether you want to save etc.

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
}
Andy Clarke
Thanks! I am in the process of creating User Controls to handle the validation. Like RequiredTextBox control, RegularExpressionTextBox etc.
azamsharp