// --------------------------------------------------------------------------------------------------------------------
// <copyright file="WarningAndErrorValidator.cs" company="Catel development team">
// Copyright (c) 2008 - 2011 Catel development team. All rights reserved.
// </copyright>
// <summary>
// Business validation type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
// Use routed events if you are NOT using MVVM and want to show errors
#define USE_ROUTED_EVENTS
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using Catel.ComponentModel;
namespace Catel.Windows.Controls
{
#region Enum
/// <summary>
/// Business validation type.
/// </summary>
public enum ValidationType
{
/// <summary>
/// Warning.
/// </summary>
Warning,
/// <summary>
/// Error.
/// </summary>
Error
}
/// <summary>
/// Validation event action.
/// </summary>
public enum ValidationEventAction
{
/// <summary>
/// Added.
/// </summary>
Added,
/// <summary>
/// Removed.
/// </summary>
Removed,
/// <summary>
/// All validation info of the specified object should be cleared.
/// </summary>
ClearAll
}
#endregion
/// <summary>
/// Control for adding business rule validation to the form. Assign a value or binding to source for the business object or
/// collection of bussiness objects to validate.
/// </summary>
public class WarningAndErrorValidator : Control
{
#region Variables
/// <summary>
/// List of objects that are currently being validated.
/// </summary>
private readonly Dictionary<object, ValidationData> _objectValidation = new Dictionary<object, ValidationData>();
private readonly object _objectValidationLock = new object();
#endregion
#region Constructor & destructor
/// <summary>
/// Initializes static members of the <see cref="WarningAndErrorValidator"/> class.
/// </summary>
static WarningAndErrorValidator()
{
#if !SILVERLIGHT && USE_ROUTED_EVENTS
ValidationEvent = EventManager.RegisterRoutedEvent("ValidationEvent", RoutingStrategy.Bubble, typeof(EventHandler<ValidationEventArgs>), typeof(WarningAndErrorValidator));
#endif
}
/// <summary>
/// Initializes a new instance of the <see cref="WarningAndErrorValidator"/> class.
/// </summary>
public WarningAndErrorValidator()
{
#if !SILVERLIGHT
Focusable = false;
#endif
Loaded += delegate { Initialize(); };
Unloaded += delegate { CleanUp(); };
}
#endregion
#region Properties
/// <summary>
/// Source for validation. This can be an business object which implements <see cref="IDataErrorInfo"/>
/// and <see cref="INotifyPropertyChanged"/> or an <see cref="IEnumerable"/> containing bussiness objects.
/// In case of a <see cref="IEnumerable"/> then the content should be static or the interface <see cref="System.Collections.ObjectModel.ObservableCollection{T}"/>.
/// </summary>
/// <remarks>
/// Wrapper for the Source dependency property.
/// </remarks>
public object Source
{
get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
/// <summary>
/// DependencyProperty definition as the backing store for Source.
/// </summary>
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(object), typeof(WarningAndErrorValidator),
new PropertyMetadata(null, (sender, e) => ((WarningAndErrorValidator)sender).UpdateSource(e.OldValue, e.NewValue)));
#endregion
#region Events
#if SILVERLIGHT && USE_ROUTED_EVENTS
/// <summary>
/// Occurs when validation is triggered.
/// </summary>
public event EventHandler<ValidationEventArgs> Validation;
#else
/// <summary>
/// Routed event for handling warnings.
/// </summary>
public static RoutedEvent ValidationEvent { get; private set; }
#endif
#endregion
#region Methods
/// <summary>
/// Initializes this instance. Loads all the errors and warnings that were added when the control was not yet loaded.s
/// </summary>
private void Initialize()
{
if (Source != null)
{
UpdateSource(null, Source);
}
}
/// <summary>
/// Cleans up.
/// </summary>
private void CleanUp()
{
List<object> objects = new List<object>();
lock (_objectValidationLock)
{
objects.AddRange(_objectValidation.Keys);
}
foreach (object obj in objects)
{
if (obj is IEnumerable)
{
RemoveObjectsFromWatchList(obj as IEnumerable);
}
else if (obj is INotifyPropertyChanged)
{
RemoveObjectFromWatchList(obj);
}
}
_objectValidation.Clear();
}
/// <summary>
/// Updates the source.
/// </summary>
/// <param name="oldValue">The old value.</param>
/// <param name="newValue">The new value.</param>
private void UpdateSource(object oldValue, object newValue)
{
if (oldValue is IEnumerable)
{
RemoveObjectsFromWatchList(oldValue as IEnumerable);
}
else if (oldValue is INotifyPropertyChanged)
{
RemoveObjectFromWatchList(oldValue);
}
#if !SILVERLIGHT
if (!IsLoaded)
{
return;
}
#endif
if (newValue is IEnumerable)
{
AddObjectsToWatchList(newValue as IEnumerable, newValue as IEnumerable);
}
else if (newValue is INotifyPropertyChanged)
{
AddObjectToWatchList(newValue);
}
}
/// <summary>
/// Adds an <see cref="IEnumerable"/> of objects to the watch list.
/// </summary>
/// <param name="values">The values to add to the watch list.</param>
/// <param name="parentEnumerable">The parent enumerable. <c>Null</c> if the object does not belong to an enumerable.</param>
private void AddObjectsToWatchList(IEnumerable values, IEnumerable parentEnumerable)
{
foreach (object value in values)
{
AddObjectToWatchList(value, parentEnumerable);
}
// Supports IObservableCollection through INotifyCollectionChanged and support IEntityCollectionCore
INotifyCollectionChanged iNotifyCollectionChanged = values as INotifyCollectionChanged;
if (iNotifyCollectionChanged != null)
{
iNotifyCollectionChanged.CollectionChanged += iNotifyCollectionChanged_CollectionChanged;
AddObjectToWatchList(parentEnumerable);
}
}
/// <summary>
/// Adds the object to the watch list.
/// </summary>
/// <param name="value">The object to add to the watch list.</param>
private void AddObjectToWatchList(object value)
{
AddObjectToWatchList(value, null);
}
/// <summary>
/// Adds the object to the watch list.
/// </summary>
/// <param name="value">The object to add to the watch list.</param>
/// <param name="parentEnumerable">The parent enumerable. <c>Null</c> if the object does not belong to an enumerable.</param>
private void AddObjectToWatchList(object value, IEnumerable parentEnumerable)
{
if (value == null)
{
return;
}
lock (_objectValidationLock)
{
if (!_objectValidation.ContainsKey(value))
{
INotifyPropertyChanged iNotifyPropertyChanged = value as INotifyPropertyChanged;
if (iNotifyPropertyChanged != null)
{
iNotifyPropertyChanged.PropertyChanged += iNotifyPropertyChanged_PropertyChanged;
}
}
}
CheckObjectValidation(value, null, parentEnumerable);
}
/// <summary>
/// Removes an <see cref="IEnumerable"/> of objects from the watch list.
/// </summary>
/// <param name="values">The values to remove from the watch list.</param>
private void RemoveObjectsFromWatchList(IEnumerable values)
{
foreach (object value in values)
{
RemoveObjectFromWatchList(value);
}
// Supports IObservableCollection through INotifyCollectionChanged and support IEntityCollectionCore
INotifyCollectionChanged iNotifyCollectionChanged = values as INotifyCollectionChanged;
if (iNotifyCollectionChanged != null)
{
iNotifyCollectionChanged.CollectionChanged -= iNotifyCollectionChanged_CollectionChanged;
RemoveObjectFromWatchList(values);
}
}
/// <summary>
/// Removes the object from watch list.
/// </summary>
/// <param name="value">The object to remove from the watch list.</param>
private void RemoveObjectFromWatchList(object value)
{
if (value == null)
{
return;
}
INotifyPropertyChanged iNotifyPropertyChanged = value as INotifyPropertyChanged;
if (iNotifyPropertyChanged != null)
{
iNotifyPropertyChanged.PropertyChanged -= iNotifyPropertyChanged_PropertyChanged;
}
RaiseBusinessValidationWarningOrError(value, string.Empty, ValidationEventAction.ClearAll, ValidationType.Warning);
RaiseBusinessValidationWarningOrError(value, string.Empty, ValidationEventAction.ClearAll, ValidationType.Error);
lock (_objectValidationLock)
{
_objectValidation.Remove(value);
}
}
/// <summary>
/// Checks a entity that either implements the <see cref="IDataWarningInfo"/> or <see cref="IDataErrorInfo"/> on warnings and errors.
/// </summary>
/// <param name="value">The object to check.</param>
/// <param name="propertyChanged">The propery that has been changed. <c>null</c> if no specific property has changed.</param>
/// <param name="parentEnumerable">The parent enumerable. <c>Null</c> if the object does not belong to an enumerable.</param>
/// <remarks>
/// Internally calls the generic method with the same name.
/// </remarks>
private void CheckObjectValidation(object value, string propertyChanged, IEnumerable parentEnumerable)
{
ValidationData currentValidationData;
ValidationData oldValidationData;
if (value == null)
{
return;
}
lock (_objectValidationLock)
{
if (!_objectValidation.ContainsKey(value))
{
_objectValidation.Add(value, new ValidationData(parentEnumerable));
}
currentValidationData = _objectValidation[value];
oldValidationData = (ValidationData)currentValidationData.Clone();
}
#region Warnings - fields
CheckObjectValidationForFields(value, propertyChanged, currentValidationData.FieldWarnings, ValidationType.Warning);
#endregion
#region Warnings - business
currentValidationData.BusinessWarnings.Clear();
string businessWarning = GetWarningOrError(value, ValidationType.Warning);
if (!string.IsNullOrEmpty(businessWarning))
{
currentValidationData.BusinessWarnings.Add(new BusinessWarningOrErrorInfo(businessWarning));
}
#endregion
#region Errors - fields
CheckObjectValidationForFields(value, propertyChanged, currentValidationData.FieldErrors, ValidationType.Error);
#endregion
#region Errors - business
currentValidationData.BusinessErrors.Clear();
string businessError = GetWarningOrError(value, ValidationType.Error);
if (!string.IsNullOrEmpty(businessError))
{
currentValidationData.BusinessErrors.Add(new BusinessWarningOrErrorInfo(businessError));
}
#endregion
RaiseEventsForDifferences(value, oldValidationData, currentValidationData);
}
/// <summary>
/// Checks the object validation for fields warnings or errors.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="propertyChanged">The property changed.</param>
/// <param name="infoList">The info list containing the warning or error info.</param>
/// <param name="validationType">Type of the validation.</param>
private static void CheckObjectValidationForFields(object value, string propertyChanged, ObservableCollection<FieldWarningOrErrorInfo> infoList,
ValidationType validationType)
{
if (string.IsNullOrEmpty(propertyChanged))
{
infoList.Clear();
}
else
{
for (int i = 0; i < infoList.Count; i++)
{
if (string.Compare(infoList[i].Field, propertyChanged) == 0)
{
infoList.RemoveAt(i);
}
}
}
Dictionary<string, string> fieldWarningsOrErrors = CheckFieldWarningsOrErrors(value, propertyChanged, validationType);
foreach (KeyValuePair<string, string> fieldWarningOrError in fieldWarningsOrErrors)
{
FieldWarningOrErrorInfo fieldWarningOrErrorInfo = new FieldWarningOrErrorInfo(fieldWarningOrError.Key, fieldWarningOrError.Value);
if (!infoList.Contains(fieldWarningOrErrorInfo))
{
infoList.Add(new FieldWarningOrErrorInfo(fieldWarningOrError.Key, fieldWarningOrError.Value));
}
}
}
/// <summary>
/// Checks the field warnings or errors.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="propertyChanged">The property changed.</param>
/// <param name="validationType">Type of the validation.</param>
/// <returns>
/// List of warnings or errors returned by the object.
/// </returns>
private static Dictionary<string, string> CheckFieldWarningsOrErrors(object value, string propertyChanged, ValidationType validationType)
{
Dictionary<string, string> warningsOrErrors = new Dictionary<string, string>();
IDataWarningInfo iDataWarningInfo = value as IDataWarningInfo;
if ((validationType == ValidationType.Warning) && (iDataWarningInfo == null))
{
return warningsOrErrors;
}
IDataErrorInfo iDataErrorInfo = value as IDataErrorInfo;
if ((validationType == ValidationType.Error) && (iDataErrorInfo == null))
{
return warningsOrErrors;
}
List<string> propertiesToCheck = new List<string>();
if (!string.IsNullOrEmpty(propertyChanged))
{
propertiesToCheck.Add(propertyChanged);
}
else
{
Type type = value.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
propertiesToCheck.Add(property.Name);
}
}
foreach (string property in propertiesToCheck)
{
string warningOrError = string.Empty;
switch (validationType)
{
case ValidationType.Warning:
warningOrError = iDataWarningInfo[property];
break;
case ValidationType.Error:
warningOrError = iDataErrorInfo[property];
break;
}
if (!string.IsNullOrEmpty(warningOrError))
{
warningsOrErrors[property] = warningOrError;
}
}
return warningsOrErrors;
}
/// <summary>
/// Gets the warning or error message for the object.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="type">The type.</param>
/// <returns>
/// Warning or error message formatted for the object.
/// </returns>
private static string GetWarningOrError(object value, ValidationType type)
{
string message = null;
switch (type)
{
case ValidationType.Warning:
if (value is IDataWarningInfo)
{
message = ((IDataWarningInfo)value).Warning;
}
break;
case ValidationType.Error:
if (value is IDataErrorInfo)
{
message = ((IDataErrorInfo)value).Error;
}
break;
}
return !string.IsNullOrEmpty(message) ? message : null;
}
/// <summary>
/// Raises the events for differences.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="oldValidationData">The old validation data.</param>
/// <param name="newValidationData">The new validation data.</param>
private void RaiseEventsForDifferences(object value, ValidationData oldValidationData, ValidationData newValidationData)
{
#region Warnings - fields
RaiseEventsForDifferencesInFields(value, oldValidationData.FieldWarnings, newValidationData.FieldWarnings, ValidationType.Warning);
#endregion
#region Warnings - business
RaiseEventsForDifferencesInBusiness(value, oldValidationData.BusinessWarnings, newValidationData.BusinessWarnings, ValidationType.Warning);
#endregion
#region Errors - fields
RaiseEventsForDifferencesInFields(value, oldValidationData.FieldErrors, newValidationData.FieldErrors, ValidationType.Error);
#endregion
#region Errors - business
RaiseEventsForDifferencesInBusiness(value, oldValidationData.BusinessErrors, newValidationData.BusinessErrors, ValidationType.Error);
#endregion
}
/// <summary>
/// Raises the events for differences in fields.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="oldFieldData">The old field data.</param>
/// <param name="newFieldData">The new field data.</param>
/// <param name="validationType">Type of the validation.</param>
private void RaiseEventsForDifferencesInFields(object value, ICollection<FieldWarningOrErrorInfo> oldFieldData,
ICollection<FieldWarningOrErrorInfo> newFieldData, ValidationType validationType)
{
foreach (FieldWarningOrErrorInfo info in oldFieldData)
{
if (!newFieldData.Contains(info))
{
RaiseBusinessValidationWarningOrError(value, info.Message, ValidationEventAction.Removed, validationType);
}
}
foreach (FieldWarningOrErrorInfo info in newFieldData)
{
if (!oldFieldData.Contains(info))
{
RaiseBusinessValidationWarningOrError(value, info.Message, ValidationEventAction.Added, validationType);
}
}
}
/// <summary>
/// Raises the events for differences in business.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="oldBusinessData">The old business data.</param>
/// <param name="newBusinessData">The new business data.</param>
/// <param name="validationType">Type of the validation.</param>
private void RaiseEventsForDifferencesInBusiness(object value, ICollection<BusinessWarningOrErrorInfo> oldBusinessData,
ICollection<BusinessWarningOrErrorInfo> newBusinessData, ValidationType validationType)
{
foreach (BusinessWarningOrErrorInfo info in oldBusinessData)
{
if (!newBusinessData.Contains(info))
{
RaiseBusinessValidationWarningOrError(value, info.Message, ValidationEventAction.Removed, validationType);
}
}
foreach (BusinessWarningOrErrorInfo info in newBusinessData)
{
if (!oldBusinessData.Contains(info))
{
RaiseBusinessValidationWarningOrError(value, info.Message, ValidationEventAction.Added, validationType);
}
}
}
/// <summary>
/// Raises an validation warning or error event.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="message">A message.</param>
/// <param name="action">A action for handling the error event.</param>
/// <param name="type">The type.</param>
private void RaiseBusinessValidationWarningOrError(object value, string message, ValidationEventAction action, ValidationType type)
{
#if !SILVERLIGHT && USE_ROUTED_EVENTS
ValidationRule validationRule = null;
switch (type)
{
case ValidationType.Warning:
validationRule = new BusinessWarningValidationRule();
break;
case ValidationType.Error:
validationRule = new BusinessErrorValidationRule();
break;
}
ValidationError validationError = new ValidationError(validationRule, value);
validationError.ErrorContent = message;
ValidationEventArgs newEventArgs = new ValidationEventArgs(validationError, action, type);
RaiseEvent(newEventArgs);
#else
ValidationEventArgs newEventArgs = new ValidationEventArgs(value, message, action, type);
if (Validation != null)
{
Validation(this, newEventArgs);
}
#endif
}
/// <summary>
/// Handling changes of properties within entity.
/// </summary>
/// <param name="sender">A sender.</param>
/// <param name="e">The event args.</param>
private void iNotifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
CheckObjectValidation(sender, e.PropertyName, null);
}
/// <summary>
/// Handling change of collection updating connections and error messages.
/// </summary>
/// <param name="sender">A sender.</param>
/// <param name="e">Event args.</param>
private void iNotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// If the action is "reset", no OldItems will be available, so clear all items manually
if (e.Action == NotifyCollectionChangedAction.Reset)
{
Collection<object> itemsToRemove = new Collection<object>();
lock (_objectValidationLock)
{
foreach (KeyValuePair<object, ValidationData> validationData in _objectValidation)
{
if (validationData.Value.ParentEnumerable == sender)
{
itemsToRemove.Add(validationData.Key);
}
}
}
// Remove the items that should be removed (outside the lock)
foreach (object itemToRemove in itemsToRemove)
{
RemoveObjectFromWatchList(itemToRemove);
}
return;
}
IEnumerable newItems = e.NewItems;
IEnumerable oldItems = e.OldItems;
if (oldItems != null)
{
RemoveObjectsFromWatchList(oldItems);
}
if (newItems != null)
{
AddObjectsToWatchList(newItems, sender as IEnumerable);
}
}
#if !SILVERLIGHT && USE_ROUTED_EVENTS
/// <summary>
/// Add an handler to routed event for validation events.
/// </summary>
/// <param name="element">A dependency object element.</param>
/// <param name="handler">A handler.</param>
/// <exception cref="ArgumentNullException">when <paramref name="element"/> is <c>null</c>.</exception>
public static void AddValidationHandler(DependencyObject element, EventHandler<ValidationEventArgs> handler)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
UIElement element2 = element as UIElement;
if (element2 != null)
{
element2.AddHandler(ValidationEvent, handler);
}
else
{
ContentElement contentElement = element as ContentElement;
if (contentElement != null)
{
contentElement.AddHandler(ValidationEvent, handler);
}
else
{
UIElement3D elementd = element as UIElement3D;
if (elementd == null)
{
throw new ArgumentException(string.Format("Invalid_IInputElement {0}", element.GetType()));
}
elementd.AddHandler(ValidationEvent, handler);
}
}
}
/// <summary>
/// Removes the validation handler.
/// </summary>
/// <param name="element">The element.</param>
/// <param name="handler">The handler.</param>
/// <exception cref="ArgumentNullException">when <paramref name="element"/> is <c>null</c>.</exception>
public static void RemoveValidationHandler(DependencyObject element, EventHandler<ValidationEventArgs> handler)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
UIElement element2 = element as UIElement;
if (element2 != null)
{
element2.RemoveHandler(ValidationEvent, handler);
}
else
{
ContentElement contentElement = element as ContentElement;
if (contentElement != null)
{
contentElement.RemoveHandler(ValidationEvent, handler);
}
else
{
UIElement3D elementd = element as UIElement3D;
if (elementd == null)
{
throw new ArgumentException(string.Format("Invalid_IInputElement {0}", element.GetType()));
}
elementd.RemoveHandler(ValidationEvent, handler);
}
}
}
#endif
#endregion
}
#region Data classes
/// <summary>
/// Class containing all validation info (thus warnings and errors) about a specific object.
/// </summary>
internal class ValidationData
{
#region Constructor & destructor
/// <summary>
/// Initializes a new instance of the <see cref="ValidationData"/> class.
/// </summary>
/// <param name="parentEnumerable">The parent ParentEnumerable. <c>Null</c> if the object does not belong to an enumerable.</param>
public ValidationData(IEnumerable parentEnumerable)
{
FieldWarnings = new ObservableCollection<FieldWarningOrErrorInfo>();
BusinessWarnings = new ObservableCollection<BusinessWarningOrErrorInfo>();
FieldErrors = new ObservableCollection<FieldWarningOrErrorInfo>();
BusinessErrors = new ObservableCollection<BusinessWarningOrErrorInfo>();
ParentEnumerable = parentEnumerable;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the parent enumerable.
/// </summary>
/// <value>The parent enumerable.</value>
public IEnumerable ParentEnumerable { get; private set; }
/// <summary>
/// Gets the field warnings.
/// </summary>
/// <value>The field warnings.</value>
public ObservableCollection<FieldWarningOrErrorInfo> FieldWarnings { get; private set; }
/// <summary>
/// Gets the business warnings.
/// </summary>
/// <value>The business warnings.</value>
public ObservableCollection<BusinessWarningOrErrorInfo> BusinessWarnings { get; private set; }
/// <summary>
/// Gets the field errors.
/// </summary>
/// <value>The field errors.</value>
public ObservableCollection<FieldWarningOrErrorInfo> FieldErrors { get; private set; }
/// <summary>
/// Gets the business errors.
/// </summary>
/// <value>The business errors.</value>
public ObservableCollection<BusinessWarningOrErrorInfo> BusinessErrors { get; private set; }
#endregion
#region Methods
/// <summary>
/// Clears the warnings and errors.
/// </summary>
public void ClearWarningsAndErrors()
{
BusinessWarnings.Clear();
FieldWarnings.Clear();
BusinessErrors.Clear();
FieldErrors.Clear();
}
/// <summary>
/// Creates a new object that is a copy of the current instance.
/// </summary>
/// <returns>
/// A new object that is a copy of this instance.
/// </returns>
public object Clone()
{
ValidationData validationData = new ValidationData(ParentEnumerable);
validationData.FieldWarnings = new ObservableCollection<FieldWarningOrErrorInfo>(FieldWarnings);
validationData.BusinessWarnings = new ObservableCollection<BusinessWarningOrErrorInfo>(BusinessWarnings);
validationData.FieldErrors = new ObservableCollection<FieldWarningOrErrorInfo>(FieldErrors);
validationData.BusinessErrors = new ObservableCollection<BusinessWarningOrErrorInfo>(BusinessErrors);
return validationData;
}
#endregion
}
/// <summary>
/// Information class about business warnings and errors.
/// </summary>
internal class BusinessWarningOrErrorInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="BusinessWarningOrErrorInfo"/> class.
/// </summary>
/// <param name="message">The message.</param>
public BusinessWarningOrErrorInfo(string message)
{
Message = message;
}
#region Properties
/// <summary>
/// Gets the message.
/// </summary>
/// <value>The message.</value>
public string Message { get; private set; }
#endregion
#region Methods
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="T:System.NullReferenceException">
/// The <paramref name="obj"/> parameter is null.
/// </exception>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != GetType())
{
return false;
}
if (obj.GetHashCode() != GetHashCode())
{
return false;
}
return true;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return string.Format(CultureInfo.InvariantCulture, "{0}", Message).GetHashCode();
}
#endregion
}
/// <summary>
/// Information class about field warnings and errors.
/// </summary>
internal class FieldWarningOrErrorInfo
{
/// <summary>
/// Initializes a new instance of the <see cref="FieldWarningOrErrorInfo"/> class.
/// </summary>
/// <param name="field">The field.</param>
/// <param name="message">The message.</param>
public FieldWarningOrErrorInfo(string field, string message)
{
Field = field;
Message = message;
}
#region Properties
/// <summary>
/// Gets the field.
/// </summary>
/// <value>The field.</value>
public string Field { get; private set; }
/// <summary>
/// Gets the message.
/// </summary>
/// <value>The message.</value>
public string Message { get; private set; }
#endregion
#region Methods
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="T:System.NullReferenceException">
/// The <paramref name="obj"/> parameter is null.
/// </exception>
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj.GetType() != GetType())
{
return false;
}
if (obj.GetHashCode() != GetHashCode())
{
return false;
}
return true;
}
/// <summary>
/// Returns a hash code for this instance.
/// </summary>
/// <returns>
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
/// </returns>
public override int GetHashCode()
{
return string.Format(CultureInfo.InvariantCulture, "{0}|{1}", Field, Message).GetHashCode();
}
#endregion
}
#endregion
#region Validation rules
#if !SILVERLIGHT
/// <summary>
/// Validation rule class for handling bussiness warning validation.
/// </summary>
public class BusinessWarningValidationRule : ValidationRule
{
/// <summary>
/// When overridden in a derived class, performs validation checks on a value.
/// </summary>
/// <param name="value">The value from the binding target to check.</param>
/// <param name="cultureInfo">The culture to use in this rule.</param>
/// <returns>
/// A <see cref="T:System.Windows.Controls.ValidationResult"/> object.
/// </returns>
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
IDataWarningInfo info = (IDataWarningInfo)value;
string message = info.Warning;
return (!string.IsNullOrEmpty(message)) ? new ValidationResult(false, message) : ValidationResult.ValidResult;
}
}
/// <summary>
/// Validation rule class for handling bussiness error validation.
/// </summary>
public class BusinessErrorValidationRule : ValidationRule
{
/// <summary>
/// When overridden in a derived class, performs validation checks on a value.
/// </summary>
/// <param name="value">The value from the binding target to check.</param>
/// <param name="cultureInfo">The culture to use in this rule.</param>
/// <returns>
/// A <see cref="T:System.Windows.Controls.ValidationResult"/> object.
/// </returns>
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
IDataErrorInfo info = (IDataErrorInfo)value;
string message = info.Error;
return (!string.IsNullOrEmpty(message)) ? new ValidationResult(false, message) : ValidationResult.ValidResult;
}
}
#endif
#endregion
#region Event args
/// <summary>
/// Event arguments for event <see cref="WarningAndErrorValidator"/> Validation.
/// </summary>
public class ValidationEventArgs :
#if !SILVERLIGHT && USE_ROUTED_EVENTS
RoutedEventArgs
#else
EventArgs
#endif
{
#region Variables
#endregion
#region Constructor & destructor
#if !SILVERLIGHT && USE_ROUTED_EVENTS
/// <summary>
/// Initializes a new instance of the <see cref="ValidationEventArgs"/> class.
/// </summary>
/// <param name="validationError">A validation error.</param>
/// <param name="action">An action causes the event.</param>
/// <param name="type">The type.</param>
internal ValidationEventArgs(ValidationError validationError, ValidationEventAction action, ValidationType type)
{
RoutedEvent = WarningAndErrorValidator.ValidationEvent;
Error = validationError;
Action = action;
Type = type;
}
#else
/// <summary>
/// Initializes a new instance of the <see cref="ValidationEventArgs"/> class.
/// </summary>
/// <param name="value">The value that contains the warning or error.</param>
/// <param name="message">The actual warning or error message.</param>
/// <param name="action">The action of the validation event.</param>
/// <param name="type">The type of validation.</param>
internal ValidationEventArgs(object value, string message, ValidationEventAction action, ValidationType type)
{
Value = value;
Message = message;
Action = action;
Type = type;
}
#endif
#endregion
#region Properties
#if !SILVERLIGHT && USE_ROUTED_EVENTS
/// <summary>
/// An error.
/// </summary>
public ValidationError Error { get; private set; }
#else
/// <summary>
/// Gets the value that contains the warning or error.
/// </summary>
/// <value>The value that contains the warning or error.</value>
public object Value { get; private set; }
/// <summary>
/// Gets the actual warning or error message.
/// </summary>
/// <value>The message.</value>
public string Message { get; private set; }
#endif
/// <summary>
/// A action for handling event.
/// </summary>
public ValidationEventAction Action { get; private set; }
/// <summary>
/// Gets the type of the validation.
/// </summary>
/// <value>The type of the validation.</value>
public ValidationType Type { get; private set; }
#endregion
#region Methods
#if !SILVERLIGHT && USE_ROUTED_EVENTS
/// <summary>
/// Invokes the event.
/// </summary>
/// <param name="genericHandler">A handler.</param>
/// <param name="genericTarget">A target.</param>
protected override void InvokeEventHandler(Delegate genericHandler, object genericTarget)
{
EventHandler<ValidationEventArgs> handler = (EventHandler<ValidationEventArgs>)genericHandler;
handler(genericTarget, this);
}
#endif
#endregion
}
#endregion
}