WPF TextBox Binding Validation Rules not Firing on LostFocus When TextBox is Empty

textboxvalidationwpf

We all know how sucky WPF validation is out of the box. I am trying a very simple thing and for some reason it is always failing. I have a TextBox and my only requirement is to validate that the user inputs something in the TextBox. The TextBox is bound to a Customer object with FirstName and LastName properties.

Here is the XAML code:

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

Here is the Customer class FirstName property:

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

                OnPropertyChanged("FirstName");
            }
        }

Even though I am throwing an exception if the FirstName (value) is null or empty it is only handled if I type something in the TextBox and then delete it and then tab off. The reason is that it is dependent on the property changed event. But even if I put that TextBox binding on Focus it does not fire the validation.

UPDATE:

One of the ugliest ways to handle this issue is to assign the String.Empty to the TextBoxes on the Window.Loaded event:

 void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // get all the textboxes and set the property to empty strings! 

            txtFirstName.Text = String.Empty;
            txtLastName.Text = String.Empty; 
        }

Here is the code for binding:

 public AddCustomerWindow()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(AddCustomerWindow_Loaded);

            gvAddCustomer.DataContext = new Customer();  
        }

Best Answer

The solution I use is to wire-up the controls to update their own binding sources whenever they lose focus. This makes the controls validate after the user has tabbed or clicked through them once.

public AddCustomerWindow()
{
    InitializeComponent();
    this.Loaded += AddCustomerWindow_Loaded;
    gvAddCustomer.DataContext = new Customer();  
}

void AddCustomerWindow_Loaded(object sender, RoutedEventArgs e)
{
    // Manually wire up textboxes of specific interest.
    UpdateSourceOnLostFocus(new TextBox[] {txtFirstName, txtLastName},
                            TextBox.TextProperty);

    // Or, wire up ALL controls of a given type with the help of Telerik's
    // ChildrenOfType extension, using Linq to filter the list where appropriate.
    UpdateSourceOnLostFocus(this.ChildrenOfType<RadDatePicker>()
                                .Where(picker => !picker.IsReadOnly), 
                            RadDatePicker.SelectedValueProperty);
}

void UpdateSourceOnLostFocus<T>(IEnumerable<T> controls, DependencyProperty prop) where T : Control
{
    controls.ToList()
        .ForEach(ctrl =>
        {
            var binding = BindingOperations.GetBindingExpression(ctrl, prop);
            if (binding != null)
                ctrl.LostFocus += (sender, args) => binding.UpdateSource();
        });
}

If you also want the controls to validate on load - i.e., before the user tabs/clicks through them - then you could write an UpdateSourceNow method similar to the one above, and call it from AddCustomerWindow_Loaded instead.

However, I prefer to wait until each control is visited, because it can be somewhat alarming to users if they immediately see a screen full of red boxes when they want to enter a new Model object instance.

Related Topic