Information
The topic you requested is included in another documentation set. For convenience, it's displayed below. Choose Switch to see the topic in its original location.

INotifyDataErrorInfo Interface

Defines members that data entity classes can implement to provide custom synchronous and asynchronous validation support.

Namespace:  System.ComponentModel
Assembly:  System.Windows (in System.Windows.dll)

public interface INotifyDataErrorInfo

The INotifyDataErrorInfo type exposes the following members.

  NameDescription
Public propertySupported by Silverlight for Windows PhoneHasErrorsGets a value that indicates whether the entity has validation errors.
Top

  NameDescription
Public methodSupported by Silverlight for Windows PhoneGetErrorsGets the validation errors for a specified property or for the entire entity.
Top

  NameDescription
Public eventSupported by Silverlight for Windows PhoneErrorsChangedOccurs when the validation errors have changed for a property or for the entire entity.
Top

This interface enables data entity classes to implement custom validation rules and expose validation results to the user interface. You typically implement this interface to provide asynchronous validation logic such as server-side validation. This interface also supports custom error objects, multiple errors per property, cross-property errors, and entity-level errors.

Cross-property errors are errors that affect multiple properties. You can associate these errors with one or all of the affected properties, or you can treat them as entity-level errors. Entity-level errors are errors that either affect multiple properties or affect the entire entity without affecting a particular property.

Silverlight 4 also supports the IDataErrorInfo interface, which enables simple validation on the client only. In general, new entity classes for Silverlight should implement INotifyDataErrorInfo for the added flexibility instead of implementing IDataErrorInfo. The IDataErrorInfo support enables you to use many existing entity classes that are written for the full .NET Framework. Other validation options are also available, such as the use of data attributes. For more information, see Data Binding,

INotifyDataErrorInfo Members

The INotifyDataErrorInfo.HasErrors property indicates whether there are currently any validation errors for the entity. The GetErrors method returns an IEnumerable that contains validation errors for the specified property or for the entire entity. You can implement this method to report cross-property errors at the entity level or for each affected property, depending on your reporting needs.

The IEnumerable of errors for a property or entity can change or be replaced as asynchronous validation rules finish processing. Whenever the IEnumerable changes, the ErrorsChanged event must occur. Event handlers can retrieve new HasErrors and GetErrors values to update the UI validation feedback for the affected property or entity.

The validation errors returned by the GetErrors method can be of any type. However, if you implement a custom error type, be sure to override the ToString method to return an error message. Silverlight uses this string in its default error reporting.

Custom error objects are useful when you provide custom error reporting in the user interface. For example, you can create a template for the reporting ToolTip that binds to an ErrorLevel property in order to display warnings in yellow and critical errors in red.

Data Binding Support

The Silverlight binding engine provides built-in support for INotifyDataErrorInfo. To enable this support, simply bind a control to a property of an entity that implements INotifyDataErrorInfo or bind a control to the entity itself. The Binding.ValidatesOnNotifyDataErrors property must also be true, which it is by default.

The binding engine calls GetErrors to retrieve any initial errors for a bound property or entity. If the Binding.Mode property value is not OneTime, the binding engine handles the ErrorsChanged event to monitor for updates. Whenever the errors change for a bound property or entity, the binding engine calls GetErrors to retrieve the updated errors. Note that the binding engine never uses the HasErrors property, although you can use it in custom error reporting.

The binding engine uses the validation results to update the Validation.Errors collection for that binding. First, the binding engine removes any existing errors for the bound property that originate from INotifyDataErrorInfo validation. Then, if the new value is not valid, the binding engine adds a new error for each error that is returned by the GetErrors method.

INotifyDataErrorInfo errors do not interfere with other errors that originate from IDataErrorInfo. However, the binding engine will not expose an INotifyDataErrorInfo error when an exception-based error is active.

Error Reporting

The Validation.Errors collection provides a binding source for error reporting in the user interface. Several controls bind to this collection from their built-in error templates.

To provide custom error reporting, you can replace or modify the existing error templates. Alternately, you can handle the ErrorsChanged event and access the INotifyDataErrorInfo members directly.

You can also set the Binding.NotifyOnValidationError property to true and handle the FrameworkElement.BindingValidationError event. This event occurs each time an error is added to, or removed from, the Validation.Errors collection for a binding. Some controls, such as ValidationSummary, require this mechanism instead of using the Validation.Errors collection.

The next section describes how to implement INotifyDataErrorInfo using a basic scenario for illustration purposes. For information about implementing more complex validation scenarios, see Implementing Data Validation in Silverlight with INotifyDataErrorInfo.

The following example code demonstrates the use of INotifyDataErrorInfo to support multiple validation results per property, including critical errors and warnings. Each TextBox displays one result, with errors having priority over warnings. Additionally, a ValidationSummary control displays the complete results.

The IsIdValid and IsNameValid methods validate the Id and Name properties. These methods apply two business rules to each property and update the errors collection based on the results. The methods return false for values that produce critical errors and true for values that are valid or produce warnings only. These return values enable the property setters to avoid committing disallowed values.

The validation methods call the AddError and RemoveError methods to update the errors collection. These methods raise the ErrorsChanged event whenever they modify the errors collection.

This example implements an entity class that has only a few properties and simple business rules to ensure that key concepts are clear. However, the basic structure supports more complex rules.

For example, the INotifyDataErrorInfo implementation automatically supports asynchronous validation. This means that you can add server-side validation by calling Web services in your validation methods and updating the errors collection in callback methods.

You can also add cross-property or entity-level validation by adding appropriate errors to the errors collection. For example, you can add cross-property errors to each affected property. Alternately, you can add entity-level errors and report them in the user interface by updating the ValidationSummary.Errors collection in a BindingValidationError event handler.

This example requires a reference to System.Windows.Controls.Data.Input.dll, which provides the Label, DescriptionViewer, and ValidationSummary controls.


using System;
using System.ComponentModel;
using System.Collections.Generic;

namespace INotifyDataErrorInfoExample
{

    public class Product : INotifyDataErrorInfo
    {
        private int idValue;
        public int Id
        {
            get { return idValue; }
            set { if (IsIdValid(value) && idValue != value) idValue = value; }
        }

        private string nameValue;
        public string Name
        {
            get { return nameValue; }
            set { if (IsNameValid(value) && nameValue != value) nameValue = value; }
        }

        // Validates the Id property, updating the errors collection as needed.
        public bool IsIdValid(int value)
        {
            bool isValid = true;

            if (value < 5)
            {
                AddError("Id", ID_ERROR, false);
                isValid = false;
            }
            else RemoveError("Id", ID_ERROR);

            if (value < 10) AddError("Id", ID_WARNING, true);
            else RemoveError("Id", ID_WARNING);

            return isValid;
        }

        // Validates the Name property, updating the errors collection as needed.
        public bool IsNameValid(string value)
        {
            bool isValid = true;

            if (value.Contains(" "))
            {
                AddError("Name", NAME_ERROR, false);
                isValid = false;
            }
            else RemoveError("Name", NAME_ERROR);

            if (value.Length > 5) AddError("Name", NAME_WARNING, true);
            else RemoveError("Name", NAME_WARNING);

            return isValid;
        }

        private Dictionary<String, List<String>> errors =
            new Dictionary<string, List<string>>();
        private const string ID_ERROR = "Value cannot be less than 5.";
        private const string ID_WARNING = "Value should not be less than 10.";
        private const string NAME_ERROR = "Value must not contain any spaces.";
        private const string NAME_WARNING = "Value should be 5 characters or less.";

        // Adds the specified error to the errors collection if it is not 
        // already present, inserting it in the first position if isWarning is 
        // false. Raises the ErrorsChanged event if the collection changes. 
        public void AddError(string propertyName, string error, bool isWarning)
        {
            if (!errors.ContainsKey(propertyName))
                errors[propertyName] = new List<string>();

            if (!errors[propertyName].Contains(error))
            {
                if (isWarning) errors[propertyName].Add(error);
                else errors[propertyName].Insert(0, error);
                RaiseErrorsChanged(propertyName);
            }
        }

        // Removes the specified error from the errors collection if it is
        // present. Raises the ErrorsChanged event if the collection changes.
        public void RemoveError(string propertyName, string error)
        {
            if (errors.ContainsKey(propertyName) &&
                errors[propertyName].Contains(error))
            {
                errors[propertyName].Remove(error);
                if (errors[propertyName].Count == 0) errors.Remove(propertyName);
                RaiseErrorsChanged(propertyName);
            }
        }

        public void RaiseErrorsChanged(string propertyName)
        {
            if (ErrorsChanged != null)
                ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));
        }

        #region INotifyDataErrorInfo Members

        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;

        public System.Collections.IEnumerable GetErrors(string propertyName)
        {
            if (String.IsNullOrEmpty(propertyName) || 
                !errors.ContainsKey(propertyName)) return null;
            return errors[propertyName];
        }

        public bool HasErrors
        {
            get { return errors.Count > 0; }
        }

        #endregion
    }
}



<UserControl x:Class="INotifyDataErrorInfoExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

  <Grid x:Name="LayoutRoot" Background="White" Margin="10">

    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="200"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

    <TextBlock Grid.Column="1" Margin="3" Text="Product" FontWeight="Bold"/>

    <sdk:Label Grid.Row="1" Target="{Binding ElementName=IdTextBox}" 
      Margin="3" HorizontalAlignment="Right"/>
    <TextBox x:Name="IdTextBox" KeyDown="TextBox_KeyDown"  
      Grid.Column="1" Grid.Row="1" Margin="3" MaxLength="5" 
      Text="{Binding Id, Mode=TwoWay, ValidatesOnExceptions=True,
      NotifyOnValidationError=True}"/>
    <sdk:DescriptionViewer Grid.Column="2" Grid.Row="1" 
      Description="Id must be greater than 4 and should be greater than 9."/>

    <sdk:Label Grid.Row="2" Target="{Binding ElementName=NameTextBox}" 
      Margin="3" HorizontalAlignment="Right"/>
    <TextBox x:Name="NameTextBox" KeyDown="TextBox_KeyDown"  
      Grid.Column="1" Grid.Row="2" Margin="3" MaxLength="10" 
      Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=True}"/>
    <sdk:DescriptionViewer Grid.Column="2" Grid.Row="2" Description=
      "Name must not contain spaces and should be 5 characters or less."/>

    <sdk:ValidationSummary Grid.Row="3" Grid.ColumnSpan="2" Margin="3"/>

  </Grid>
</UserControl>



using System.Windows.Controls;

namespace INotifyDataErrorInfoExample
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            LayoutRoot.DataContext = new Product() { Id = 10, Name = "food" };
        }

        // Commits text box values when the user presses ENTER. This makes it 
        // easier to experiment with different values in the text boxes.
        private void TextBox_KeyDown(object sender, 
            System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Enter) (sender as TextBox)
                .GetBindingExpression(TextBox.TextProperty).UpdateSource();
        }
    }
}


Silverlight

Supported in: 5, 4

Silverlight for Windows Phone

Supported in: Windows Phone OS 7.1

For a list of the operating systems and browsers that are supported by Silverlight, see Supported Operating Systems and Browsers.

Community Additions

Show:
© 2014 Microsoft