Really simple WPF form data validation - how to?

2019-01-17 04:26发布

问题:

I'm having this really simple class, lets call it Customer. It look like this:

namespace TestValidation
{
     class Customer
     {
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                if (String.IsNullOrEmpty(value))
                {
                    throw new Exception("Customer name is mandatory.");
                }
            }
        }
    }
}

Now, I've created a basic form, where the user can add customers to the database. The form contain simple TextBox, bounded to the Name property of Customer, and an "Add" button.

The XAML code is:

<Window x:Class="TestValidation.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestValidation"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
<TextBox Margin="119,86,107,194" Name="CustomerName"
        Text="{Binding Path=Customer.Name, 
                ValidatesOnExceptions=True, 
                ValidatesOnDataErrors=True,
                UpdateSourceTrigger=PropertyChanged,
                NotifyOnValidationError=True}"
    />
        <Button Content="Add" HorizontalAlignment="Left" Margin="204,176,0,0" VerticalAlignment="Top" Width="74"/>
    </Grid>
</Window> 

From the setter of the Name property, you can understand that the name is mandatory for me, so I want an validation event to rise if the Name TextBox left blank. By validation rules of WPF - once the user focus out of the textbox, and there's no value over there - it should change the border color to red. For some reason - this is not happening, and I don't have a clue why. What is wrong in my process?

Now, I've read so many good articles about Validation in WPF (like Enforcing Complex Business Data Rules with WPF, Data validation in WPF and Validation in Windows Presentation Foundation), but none of them helped me solving my problem.

Eventually, I want the form to look like the form in Brian Noyes excellent article over the first link (Don't have 10 credits, so I can't attach a photo... sorry).

I'll be grateful if someone can explain to me how it really works.

Important note - I'm working with .Net framework 4, so I need a solution that suits this version.

回答1:

I would definitely recommend using IDataErrorInfo for WPF validation since WPF already understands how to use it, and its easy to implement.

To start with, add the interface to the class containing the data you want to validate. The required methods will probably look something like this:

public class Customer : IDataErrorInfo
{
    ...

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error
    {
        get { return null; }
    }

    string IDataErrorInfo.this[string columnName]
    {
        get
        {
            if (columnName == "Name")
            {
                // Validate property and return a string if there is an error
                if (string.IsNullOrEmpty(Name))
                    return "Name is Required";
            }

            // If there's no error, null gets returned
            return null;
        }
    }
    #endregion
}

Next, you need to set ValidatesOnDataErrors=True in your TextBox binding so it runs the validation whenever the Name property changes:

<TextBox Text="{Binding Path=Customer.Name, ValidatesOnDataErrors=True}" ... />

And finally, create a Validation Template in your XAML to tell WPF how to draw a validation error. Here's the style/template I usually use:

<!-- ValidatingControl Style -->
<Style TargetType="{x:Type FrameworkElement}" x:Key="ValidatingControl">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="ToolTip" Value="{Binding 
                Path=(Validation.Errors)[0].ErrorContent, 
                RelativeSource={x:Static RelativeSource.Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Also, be sure your Customer class implements INotifyPropertyChanged so it correctly responds to UI updates. I don't see that in your code, but often people leave that out for simplicity :)



回答2:

You did not specify a validation rule. The validation rule would be invoked before the control is left and then can do whatever you want to validate the inputs.

A simple example - and I guess that's what you want to do - is provided here.



回答3:

Use IDataErrorInfo for validation. this link will help you.



回答4:

I think the issue might be that your class isn't implementing INotifyPropertyChanged, so isn't binding as you're expecting.

Implement the INotifyPropertyChanged interface, raise an event when the property changed and it should work.

See http://msdn.microsoft.com/en-us/library/ms743695(v=vs.110).aspx for a walkthrough.



回答5:

<Binding Path=Name UpdateSourceTrigger="PropertyChanged">
  <Binding.ValidationRules>
    <ExceptionValidationRule />
  </Binding.ValidationRules>
</Binding>

http://msdn.microsoft.com/en-us/library/ms752347%28v=vs.110%29.aspx#what_is_data_binding

Please use this blog : prasadcsharp.blogspot.com



回答6:

I know this post is old, but here is something that worked fine with me. No lag or long coding but I used it on double values only. You may change it as you need.

 private void search_box_TextChanged(object sender, TextChangedEventArgs e)
    {
        //  box text and background to normal state if user types numbers
        search_box.Foreground = Brushes.Black;
        search_box.Background = Brushes.White;

          if (search_id.IsSelected == true)
        {
            try
            {
                //convert while user is typing
                if (string.IsNullOrEmpty(search_box.Text)==false)
              Convert.ToDouble(search_box.Text);
                search_error.Text = null;
            }

            //if user types a letter or a space or a symbol  ====>
            catch (Exception)
            {
          //  user cant type any value other than numbers as exception prevents it and clears the box text value <======
                search_box.Text = null;
                search_box.Foreground = Brushes.White;
                search_box.Background = Brushes.Red;
                search_error.Text="id is numberic value";
            }
        }

        }

Hope it helps.



回答7:

<ControlTemplate x:Key="TextBoxErrorTemplate">
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <Image Height="16" Margin="0,0,5,0" 
                    Source="Assets/warning_48.png"/>
            <AdornedElementPlaceholder x:Name="Holder"/>
        </StackPanel>
        <Label Foreground="Red" Content="{Binding ElementName=Holder, 
               Path=AdornedElement.(Validation.Errors)[0].ErrorContent}"/>
    </StackPanel>
</ControlTemplate> 


<TextBox x:Name="Box" 
         Validation.ErrorTemplate="{StaticResource TextBoxErrorTemplate}">
    <TextBox.Text>
        <Binding Path="ValueInBox" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <ValidationExpamples:DoubleRangeRule Min="0.5" Max="10"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>           
</TextBox>

IntRangeRule class:

public class IntRangeRule : ValidationRule
{
    private int _min;
    private int _max;
    public IntRangeRule()
    {
    }
    public int Min
    {
        get { return _min; }
        set { _min = value; }
    }

    public int Max
    {
        get { return _max; }
        set { _max = value; }
    }
    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        int l_input = 0;
        try
        {
            if (((string)value).Length > 0)
            {
                l_input = Int32.Parse((String)value);
            }
        }
        catch (Exception e)
        {
            return new ValidationResult(false, "Illegal characters or " + e.Message);
        }

        if ((l_input < Min) || (l_input > Max))
        {
            return new ValidationResult(false, "Please enter an value in the range: " + Min + " - " + Max + ".");
        }

        return new ValidationResult(true, null);
    }
}