views:

4369

answers:

4

Hello.

I have a WPF dialog with a couple of textboxes on it. Textboxes are bound to my business object and have WPF validation rules attached.

The problem is that user can perfectly click 'OK' button and close the dialog, without actually entering the data into textboxes. Validation rules never fire, since user didn't even attempt entering the information into textboxes.

Is it possible to force validation checks and determine if some validation rules are broken?

I would be able to do it when user tries to close the dialog and prohibit him from doing it if any validation rules are broken.

Thank you.

+15  A: 

We have this issue in our application as well. The validation only fires when bindings update, so you have to update them by hand. We do this in the Window's Loaded event:

public void Window_Loaded(object sender, RoutedEventArgs e)
{
    // we manually fire the bindings so we get the validation initially
    txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    txtCode.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}

This will make the error template (red outline) appear, and set the Validation.HasError property, which we have triggering the OK button to disable:

<Button x:Name="btnOK" Content="OK" IsDefault="True" Click="btnOK_Click">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="IsEnabled" Value="false" />
            <Style.Triggers>
                <!-- Require the controls to be valid in order to press OK -->
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding ElementName=txtName, Path=(Validation.HasError)}" Value="false" />
                        <Condition Binding="{Binding ElementName=txtCode, Path=(Validation.HasError)}" Value="false" />
                    </MultiDataTrigger.Conditions>
                    <Setter Property="IsEnabled" Value="true" />
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>
Robert Macnee
+1 Awesome, I was looking to do this almost exactly.
Andrew Barrett
+1, exactly what I was looking for. Have you adopted the title of "WPF Wizard" yet, Robert? :)
Greg D
Sweet! This is exactly what I was looking for.
Tom Alderman
This works and achieves what I was looking for, but I have no codebehind. Form logic is encapsulated in a ModelView. Since the ModelView is not supposed to have references to specific screen elements, how can this be done and still have no codebehind? Is there a way in XAML to force binding?
Kilhoffer
bump. I was beating myself silly over this one. It feels great not to be alone! :)
Bob
what if you don't have a named element? what if it's a part of a template in the ItemsControl ?
PaN1C_Showt1Me
Hello i'm a newby but i'm certain this is what i want as functionalities.Now my problem is that my application is connecting to a webservice for any of its functionalities.That means i don't have the datamodel in my app.I'm honestly looking for a way to do validation without data binding.Can somebody show me the way to follow? thanks
black sensei
Working, but I would not like to use it directly. Instead one could attach it as a 'behavior' via attached property to the bound controls.
Simpzon
A: 

Hi

I have tried this but can't get it to work. The code in the Loaded event handler doesn't trigger the validation. If I instead place it in a button event handler and press the button. Then the validation is triggered.

What am I missing?

Per Grill

I Figured it out:
I had the following lines in the Loaded event handler:

person = new Person();
this.DataContext = person;

I moved these lines to the constructor instead right after InitializeComponent() and it worked

+8  A: 

In 3.5SP1 / 3.0SP2, they also added a new property to the ValidationRule base, namely, ValidatesOnTargetUpdated="True". This will call the validation as soon as the source object is bound, rather than only when the target control is updated. That may not be exactly what you want, but it's not bad to see initially all the stuff you need to fix.

Works something like this:

<TextBox.Text>
 <Binding Path="Amount" StringFormat="C">
  <Binding.ValidationRules>
   <validation:RequiredValidationRule ErrorMessage="The pledge amount is required." ValidatesOnTargetUpdated="True"  />
   <validation:IsNumericValidationRule ErrorMessage="The pledge amount must be numeric." ValidationStep="ConvertedProposedValue" ValidatesOnTargetUpdated="True"  />
  </Binding.ValidationRules>
 </Binding>
</TextBox.Text>
Ken Smith
+1 for saving my life ;-)
Nils
This property is really great, simple and does exactly what we need.
PaN1C_Showt1Me
So glad they added this.
Simpzon
A: 

using the INotifyPropertychanged on your data object

public class MyObject : INotifyPropertyChanged
{
    string _MyPropertyToBind = string.Empty;
    public string MyPropertyToBind
    {
        get
        {
            return _MyPropertyToBind;
        }
        set
        {
            _MyPropertyToBind = value;
            NotifyPropertyChanged("MyPropertyToBind");
        }
    }

    public void NotifyPropertyChanged(string property)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

}

you can add the following code to your control

<TextBox Text="{Binding MyPropertyToBind, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >

The textbox susbscribe to the propertychanged event of the datacontext object ( MyObjet in our example) and assumes it is fired when the source data has been updated

it automatically forces the refresh to the control

No need to call yourself the UpdateTarget method

Jenzo