As the user completes a form, individual properties are validated. To validate an entire form at once, the same approach is used; however, each property is validated, one by one, until all are validated, or until one is deemed invalid.
The IsComplete
method of the DataErrorValidator
attempts to validate each known property and accepts handlers that are invoked when one of the following three conditions is met:
completeAction—The viewmodel is complete; that is, no properties have associated data-validation errors.
incompleteAction—The viewmodel is incomplete; that is, one or more properties have an associated data-validation error.
unknownAction—Determination of the viewmodel’s completeness failed because an exception was raised.
To validate each property, the DataErrorValidator
creates a list of all known property names, and as each property is validated, the property name is removed from the list, as shown in the following excerpt:
public void IsComplete(Action completeAction,
Action incompleteAction,
Action<Exception> unknownAction)
{
this.completeAction = completeAction;
this.incompleteAction = incompleteAction;
this.unknownAction = unknownAction;
try
{
if (!LockedOperations.TrySetTrue(
ref isEvaluating, isEvaluatingLock))
{
return;
}
if (propertyDictionary == null)
{
if (completeAction != null)
{
completeAction();
}
return;
}
lock (waitingForPropertiesLock)
{
waitingForProperties.Clear();
foreach (KeyValuePair<string, Func<object>> pair
in propertyDictionary)
{
waitingForProperties.Add(pair.Key);
}
}
foreach (KeyValuePair<string, Func<object>> pair
in propertyDictionary)
{
validator.BeginValidation(pair.Key, pair.Value());
}
}
catch (Exception ex)
{
isEvaluating = false;
if (unknownAction != null)
{
unknownAction(ex);
}
}
}
The LockedOperations.TrySetTrue
method provides for thread safety when reading and setting the isEvaluating
flag. If isEvaluating
is already true, the call to the IsComplete
method is ignored, as shown:
public static bool TrySetTrue(ref bool value, object valueLock)
{
ArgumentValidator.AssertNotNull(valueLock, "valueLock");
if (!value)
{
lock (valueLock)
{
if (!value)
{
value = true;
return true;
}
}
}
return false;
}
The TrySetTrue
method reduces the amount of locking related code in the IsComplete
method.
When the list of property names in the IsComplete
method is empty, or an exception is raised, the evaluation of the IsComplete
method is deemed to be complete.
At completion, one of the three IsComplete
action arguments is invoked. If the ValidationCompleteEventArgs
contains an Exception
, the unknownAction
is invoked. If there are any data validation errors for the property, the incompleteAction
is invoked. If there are no properties left to validate, the completeAction
is invoked. See the following excerpt:
void HandleValidationComplete(object sender, ValidationCompleteEventArgs e)
{
try
{
if (e.Exception == null)
{
SetPropertyErrors(e.PropertyName, e.Errors);
}
}
catch (Exception ex)
{
Debug.WriteLine("Unable to set property error." + ex);
}
if (!isEvaluating)
{
return;
}
lock (isEvaluatingLock)
{
if (!isEvaluating)
{
return;
}
try
{
bool finishedEvaluating;
lock (waitingForPropertiesLock)
{
waitingForProperties.Remove(e.PropertyName);
finishedEvaluating = waitingForProperties.Count < 1;
}
if (e.Exception != null)
{
isEvaluating = false;
if (unknownAction != null)
{
unknownAction(e.Exception);
}
}
if (e.Errors != null && e.Errors.Count() > 0)
{
isEvaluating = false;
if (incompleteAction != null)
{
incompleteAction();
}
}
if (finishedEvaluating)
{
bool success = isEvaluating;
isEvaluating = false;
if (success && completeAction != null)
{
completeAction();
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Unable to validate property." + ex);
isEvaluating = false;
}
}
}
18.118.198.61