Voici un video en mini exemple:

Voici le code:
[xml]
<Grid Height="134">
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="5*"/>
    </Grid.RowDefinitions>
    <local:ErrorProvider x:Name="errorProvider"/>
    <Label>Nom:
    <TextBox Name="text" Grid.Column="1">
        <TextBox.Text>
            <Binding Path=".">
                <Binding.ValidationRules>
                    <local:StringValidationRule Regex="A.*"/>
                </Binding.ValidationRules>
            </Binding>
        </TextBox.Text>
    </TextBox>
    <Button>
        <Button>
            <Button>
                <Button>
                </Button>
            </Button>
        </Button>
    </Button>
    <Button Grid.Column="1" Grid.Row="1" Content="Ok" Height="30"  Click="Button_Click" VerticalAlignment="Bottom"/>
</Grid>
Mon validateur : Ma chaîne dans ma TextBox doit commencer par A majuscule.
[csharp]
public class StringValidationRule : ValidationRule
{
    private string m_regex;
    public string Regex
    {
        get { return m_regex; }
        set { m_regex = value; }
    }

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        System.Text.RegularExpressions.Regex regex = new System.Text.RegularExpressions.Regex(m_regex);
        if (!regex.IsMatch(value.ToString()))
        {
            return new ValidationResult(false, "Invalid string");
        }

        return new ValidationResult(true, null);
    }
}
Mon contrôle d'erreur.
[csharp]
using System.Windows;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Collections.ObjectModel;

public class ErrorProvider : System.Windows.FrameworkElement
{
    #region Membres publics

    public class Error
    {
        public DependencyObject Object
        {
            get
            {
                return m_object;
            }
        }

        public ReadOnlyObservableCollection Errors
        {
            get
            {
                return m_errors;
            }
        }

        public Error(DependencyObject obj, ReadOnlyObservableCollection errors)
        {
            m_object = obj;
            m_errors = errors;
        }

        private DependencyObject m_object;
        private ReadOnlyObservableCollection m_errors;
    }


    public bool HasErrors
    {
        get
        {
            bool hasErrors = false;
            DoCheckHasErrors(this.Parent, ref hasErrors, null);
            return hasErrors;
        }
    }

    public ReadOnlyObservableCollection Errors
    {
        get
        {
            ObservableCollection list = new ObservableCollection();
            bool hasErrors = false;
            DoCheckHasErrors(this.Parent, ref hasErrors, list);
            return new ReadOnlyObservableCollection(list);
        }
    }

    #endregion

    #region Membres privés

    private void DoCheckHasErrors(DependencyObject element, ref bool hasErrors, ObservableCollection errors)
    {
        foreach (object child in LogicalTreeHelper.GetChildren(element))
        {
            DependencyObject depObject = child as DependencyObject;
            if (null != depObject)
            {
                if (Validation.GetHasError(depObject))
                {
                    hasErrors = true;
                    if (null != errors)
                    {
                        errors.Add(new Error(depObject, Validation.GetErrors(depObject)));
                    }
                }
                DoCheckHasErrors(depObject, ref hasErrors, errors);
            }
        }
    }

    #endregion
}
Ce code m'offre la possibilité de pouvoir avoir un contrôle d'erreurs au niveau de ma grid.
Si jamais, il y a un bug ... Maiiiiil ;-). Merci.