Again, I went through exactly the same thing this week!  I found the following on the web, wrap your text boxes in this ...
<validators:ValidatedContent Name="Validator" >
<!-- All your textboxes here -->
</validators:ValidatedContent>
Add the class below in the same place you put the other one ... and then you can call Validator.Validate() from a button click or where ever you want.  There is also an IsContentValid property you can use to decide whether you want to save etc.
public class ValidatedContent : Decorator
{
    #region Public Constructors
    /// <summary>
    /// Initializes a new instance of ValidatedContent
    /// </summary>
    public ValidatedContent()
    {
        ErrorMessages = new ObservableCollection<string>();
        Loaded += new RoutedEventHandler(OnValidatedContentLoaded);
    }
    #endregion
    #region Event Handlers
    /// <summary>
    /// Handles the loaded event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void OnValidatedContentLoaded(object sender, RoutedEventArgs e)
    {
        Queue<DependencyObject> elementQueue = new Queue<DependencyObject>();
        elementQueue.Enqueue(this.Child);
        // Iterate over all the child elements
        while (elementQueue.Count > 0)
        {
            // Dequeue the first element in the queue
            DependencyObject element = elementQueue.Dequeue();
            if (element != null)
            {
                foreach (var childElement in LogicalTreeHelper.GetChildren(element))
                {
                    if (childElement is DependencyObject)
                        elementQueue.Enqueue((DependencyObject)childElement);
                }
            }
            Control control = element as Control;
            // Mark the element as valid if it is a control
            if (control != null && GetIsRequired(element))
            {
                control.SetValue(Control.StyleProperty, RequiredControlStyle);
            }
        }
    }
    #endregion
    #region Dependency Properties
    public static readonly DependencyProperty IsContentValidProperty =
        DependencyProperty.Register("IsContentValid", typeof(bool),
        typeof(ValidatedContent), new UIPropertyMetadata(false));
    public static readonly DependencyProperty ErrorMessagesProperty =
        DependencyProperty.Register("ErrorMessages", typeof(ObservableCollection<string>),
        typeof(ValidatedContent), new UIPropertyMetadata(null));
    public static readonly DependencyProperty RequiredControlStyleProperty =
        DependencyProperty.Register("RequiredControlStyle", typeof(Style),
        typeof(ValidatedContent), new UIPropertyMetadata(null));
    public static readonly DependencyProperty IsRequiredProperty =
        DependencyProperty.RegisterAttached("IsRequired", typeof(bool),
        typeof(ValidatedContent), new UIPropertyMetadata(false));
    #endregion
    #region Public Properties
    /// <summary>
    /// Gets or sets the style to mark a required control
    /// </summary>
    public Style RequiredControlStyle
    {
        get { return (Style)GetValue(RequiredControlStyleProperty); }
        set { SetValue(RequiredControlStyleProperty, value); }
    }
    /// <summary>
    /// Gets or sets the error messages for the validated content
    /// </summary>
    public ObservableCollection<string> ErrorMessages
    {
        get { return (ObservableCollection<string>)GetValue(ErrorMessagesProperty); }
        private set { SetValue(ErrorMessagesProperty, value); }
    }
    /// <summary>
    /// Gets if the content is valid
    /// </summary>
    public bool IsContentValid
    {
        get { return (bool)GetValue(IsContentValidProperty); }
        private set { SetValue(IsContentValidProperty, value); }
    }
    #endregion
    #region Public Methods
    /// <summary>
    /// Validates the content of the decorator
    /// </summary>
    public void Validate()
    {
        IsContentValid = true;
        ErrorMessages.Clear();
        Queue<DependencyObject> elementQueue = new Queue<DependencyObject>();
        elementQueue.Enqueue(this.Child);
        // Iterate over all the child elements
        while (elementQueue.Count > 0)
        {
            // Dequeue the first element in the queue
            DependencyObject element = elementQueue.Dequeue();
            foreach (var childElement in LogicalTreeHelper.GetChildren(element))
            {
                if (childElement is DependencyObject)
                    elementQueue.Enqueue((DependencyObject)childElement);
            }
            // Validate the bindings of the element
            ValidateBindings(element);
        }
    }
    #endregion
    #region Private Methods
    /// <summary>
    /// Validates the bindings of the dependency object
    /// </summary>
    /// <param name="element"></param>
    private void ValidateBindings(DependencyObject element)
    {
        if (element != null)
        {
            Type elementType = element.GetType();
            FieldInfo[] dependencyPropertyFields = elementType.GetFields(
                BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly);
            // Iterate over all dependency properties
            foreach (FieldInfo dependencyPropertyField in dependencyPropertyFields)
            {
                DependencyProperty dependencyProperty =
                    dependencyPropertyField.GetValue(element) as DependencyProperty;
                if (dependencyProperty != null)
                {
                    Binding binding = BindingOperations.GetBinding(element, dependencyProperty);
                    BindingExpression bindingExpression = BindingOperations.GetBindingExpression(element, dependencyProperty);
                    // Issue 1822 - Extra check added to prevent null reference exceptions
                    if (binding != null && bindingExpression != null)
                    {
                        // Validate the validation rules of the binding
                        foreach (ValidationRule rule in binding.ValidationRules)
                        {
                            ValidationResult result = rule.Validate(element.GetValue(dependencyProperty),
                                CultureInfo.CurrentCulture);
                            bindingExpression.UpdateSource();
                            if (!result.IsValid)
                            {
                                ErrorMessages.Add(result.ErrorContent.ToString());
                            }
                            IsContentValid &= result.IsValid;
                        }
                    }
                }
            }
        }
    }
    #endregion
    #region Static Methods
    /// <summary>
    /// Gets the value for the IsRequired attached property
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public static bool GetIsRequired(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsRequiredProperty);
    }
    /// <summary>
    /// Sets the value for the IsRequired attached property
    /// </summary>
    /// <param name="obj"></param>
    /// <param name="value"></param>
    public static void SetIsRequired(DependencyObject obj, bool value)
    {
        obj.SetValue(IsRequiredProperty, value);
    }
    #endregion
}