16

I am trying to validate the WPF form against an object. The validation fires when I type something in the textbox lose focus come back to the textbox and then erase whatever I have written. But if I just load the WPF application and tab off the textbox without writing and erasing anything from the textbox, then it is not fired.

Here is the Customer.cs class:

public class Customer : IDataErrorInfo
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public string Error
        {
            get { throw new NotImplementedException(); }
        }
        public string this[string columnName]
        {
            get
            {
                string result = null;

                if (columnName.Equals("FirstName"))
                {
                    if (String.IsNullOrEmpty(FirstName))
                    {
                        result = "FirstName cannot be null or empty"; 
                    }
                }
                else if (columnName.Equals("LastName"))
                {
                    if (String.IsNullOrEmpty(LastName))
                    {
                        result = "LastName cannot be null or empty"; 
                    }
                }
                return result;
            }
        }
    }

And here is the WPF code:

<TextBlock Grid.Row="1" Margin="10" Grid.Column="0">LastName</TextBlock>
<TextBox Style="{StaticResource textBoxStyle}" Name="txtLastName" Margin="10"
         VerticalAlignment="Top" Grid.Row="1" Grid.Column="1">
    <Binding Source="{StaticResource CustomerKey}" Path="LastName"
             ValidatesOnExceptions="True" ValidatesOnDataErrors="True"
             UpdateSourceTrigger="LostFocus"/>         
</TextBox>
Michael Myers
  • 188,989
  • 46
  • 291
  • 292
azamsharp
  • 19,710
  • 36
  • 144
  • 222

6 Answers6

20

If you're not adverse to putting a bit of logic in your code behind, you can handle the actual LostFocus event with something like this:

.xaml

<TextBox LostFocus="TextBox_LostFocus" ....

.xaml.cs

private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
     ((Control)sender).GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
Bermo
  • 4,921
  • 1
  • 28
  • 31
  • 1
    Unfortunatelly this can be applied with MVVM logic. Any hints on how to do it when you do not want to modify your code behind? – FanaticD Apr 29 '16 at 20:07
  • 1
    I thought code in the view "exclusively for the view" with no relation with the viewmodel was ok in MVVM. – Daniel Möller Aug 17 '20 at 18:30
12

Unfortunately this is by design. WPF validation only fires if the value in the control has changed.

Unbelievable, but true. So far, WPF validation is the big proverbial pain - it's terrible.

One of the things you can do, however, is get the binding expression from the control's property and manually invoke the validations. It sucks, but it works.

cranley
  • 613
  • 1
  • 5
  • 10
6

Take a look at the ValidatesOnTargetUpdated property of ValidationRule. It will validate when the data is first loaded. This is good if you're trying to catch empty or null fields.

You'd update your binding element like this:

<Binding 
    Source="{StaticResource CustomerKey}" 
    Path="LastName" 
    ValidatesOnExceptions="True" 
    ValidatesOnDataErrors="True" 
    UpdateSourceTrigger="LostFocus">
    <Binding.ValidationRules>
        <DataErrorValidationRule
            ValidatesOnTargetUpdated="True" />
    </Binding.ValidationRules>
</Binding>
w4g3n3r
  • 967
  • 5
  • 8
  • 1
    If you check with reflector, you'll see that the 'ValidatesOnTargetUpdated' property is already set by DataErrorValidationRule to true. It calls an inherited constructor with a param which specifies this. So, adding the property assignments as shown won't make any difference. Good thought though - I've been trying to fix a similar problem and this did initially look promising. – Phil Oct 11 '09 at 01:18
  • This worked for me on a custom rule! Thanks a million buddy! – JFTxJ Sep 24 '13 at 18:21
1

I found out the best way for me to handle this was on the LostFocus event of the text box I do something like this

    private void dbaseNameTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        if (string.IsNullOrWhiteSpace(dbaseNameTextBox.Text))
        {
            dbaseNameTextBox.Text = string.Empty;
        }
    }

Then it sees an error

animuson
  • 53,861
  • 28
  • 137
  • 147
theDoke
  • 553
  • 1
  • 7
  • 18
0

The follow code loops over all the controls and validates them. Not necessarily the preferred way but seems to work. It only does TextBlocks and TextBoxes but you can easily change it.

public static class PreValidation
{

    public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }


    public static void Validate(DependencyObject depObj)
    {
        foreach(var c in FindVisualChildren<FrameworkElement>(depObj))
        {
            DependencyProperty p = null;

            if (c is TextBlock)
                p = TextBlock.TextProperty;
            else if (c is TextBox)
                p = TextBox.TextProperty;

            if (p != null && c.GetBindingExpression(p) != null) c.GetBindingExpression(p).UpdateSource();
        }

    }
}

Just call Validate on your window or control and it should pre-validate them for you.

AbstractDissonance
  • 1
  • 2
  • 16
  • 31
0

I've gone through the same problem and found an ultra simple way to resolve this : in the Loaded event of your window, simply put txtLastName.Text = String.Empty. That's it!! Since the property of your object has changed (been set to an empty string), the validation's firing !