ValidationService.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / cdf / src / NetFx40 / Tools / System.Activities.Presentation / System / Activities / Presentation / Validation / ValidationService.cs / 1591162 / ValidationService.cs

                            //---------------------------------------------------------------- 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//---------------------------------------------------------------

namespace System.Activities.Presentation.Validation 
{
    using System.Activities.Presentation.Model; 
    using System.Activities.Presentation.Services; 
    using System.Activities.Presentation.Xaml;
    using System.Activities.Statements; 
    using System.Activities.Validation;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Globalization; 
    using System.Linq;
    using System.Reflection; 
    using System.Runtime; 
    using System.Text;
    using System.Windows.Threading; 
    using Microsoft.VisualBasic.Activities;
    using Microsoft.Win32;
    using System.Activities.ExpressionParser;
    using System.Activities.Presentation.View; 
    using System.Activities.Expressions;
    using System.ComponentModel; 
    using System.Activities.XamlIntegration; 

    public enum ValidationState 
    {
        // The numeric values of each of the enum values indicate the severity of the error.
        // The higher the number is, the more severe the error. See the MarkError method in the
        // ValidationService class for example of usage. 
        Error = 3,
        Warning = 2, 
        ChildInvalid = 1, 
        Valid = 0
    } 

    internal enum ValidationReason
    {
        Unknown, 
        Load,
        Save, 
        ModelChange, 
    }
 
    [Fx.Tag.XamlVisible(false)]
    public class ValidationService
    {
        static PropertyInfo parentPropertyInfo; 

        EditingContext context; 
        ModelService modelService; 
        ModelTreeManager modelTreeManager;
        WorkflowViewService viewService; 
        IValidationErrorService errorService;

        // Dictionary which maps the activities to their error messages and indicators
        // NOTE: Valid Workflow Elements/Model Items do not appear in this dictionary, 
        // only elements which violated a constraint (Errors or Warnings or Child Validation Issues)
        Dictionary validationErrors; 
 
        // Attached properties for error visuals
        AttachedProperty validationStateProperty; 
        AttachedProperty validationMessageProperty;

        internal event EventHandler ValidationCompleted;
 
        internal class ErrorsMarkedEventArgs : EventArgs
        { 
            ICollection errors; 
            ValidationReason reason;
            ModelTreeManager modelTreeManager; 
            EditingContext context;

            public ErrorsMarkedEventArgs(ICollection errors,
                ValidationReason reason, 
                ModelTreeManager modelTreeManager,
                EditingContext context) 
            { 
                this.errors = errors;
                this.reason = reason; 
                this.modelTreeManager = modelTreeManager;
                this.context = context;
            }
 
            public ICollection Errors
            { 
                get { return this.errors; } 
            }
 
            public ValidationReason Reason
            {
                get { return this.reason; }
            } 

            public ModelTreeManager ModelTreeManager 
            { 
                get { return this.modelTreeManager; }
            } 

            public bool Handled
            {
                get; 
                set;
            } 
 
            public EditingContext Context
            { 
                get { return this.context; }
            }
        }
 
        internal event EventHandler ErrorsMarked;
 
        Sequence bodyPlaceholder; 

        ValidationSettings settings; 

        bool isValidationDisabled = false;
        const string ValidationRegKeyName = "DisableValidateOnModelItemChanged";
        const string ValidationRegKeyInitialPath = "Software\\Microsoft\\.NETFramework\\"; 
        [ThreadStatic]
        static StringBuilder errorBuilder; 
 
        private static StringBuilder ErrorBuilder
        { 
            get
            {
                if (errorBuilder == null)
                { 
                    errorBuilder = new StringBuilder();
                } 
                return errorBuilder; 
            }
        } 

        public ValidationService(EditingContext context)
        {
            this.context = context; 
            this.settings = new ValidationSettings();
            this.context.Services.Subscribe(new SubscribeServiceCallback(OnModelServiceAvailable)); 
            this.context.Services.Subscribe(new SubscribeServiceCallback(OnModelTreeManagerAvailable)); 
            this.context.Services.Subscribe(new SubscribeServiceCallback(OnErrorServiceAvailable));
            this.context.Services.Subscribe(new SubscribeServiceCallback(OnAttachedPropertiesServiceAvailable)); 
            AssemblyName currentAssemblyName = Assembly.GetExecutingAssembly().GetName();
            StringBuilder validationKeyPath = new StringBuilder(90);
            validationKeyPath.Append(ValidationRegKeyInitialPath);
            validationKeyPath.AppendFormat("{0}{1}{2}", "v", currentAssemblyName.Version.ToString(), "\\"); 
            validationKeyPath.Append(currentAssemblyName.Name);
 
            RegistryKey validationRegistryKey = Registry.CurrentUser.OpenSubKey(validationKeyPath.ToString()); 
            if (validationRegistryKey != null)
            { 
                object value = validationRegistryKey.GetValue(ValidationRegKeyName);

                this.isValidationDisabled = (value != null && string.Equals("1", value.ToString()));
 
                validationRegistryKey.Close();
            } 
        } 

        Sequence BodyPlaceholder 
        {
            get
            {
                if (null == this.bodyPlaceholder) 
                {
                    this.bodyPlaceholder = new Sequence(); 
                } 
                return this.bodyPlaceholder;
            } 
        }

        public ValidationSettings Settings
        { 
            get
            { 
                return this.settings; 
            }
        } 

        WorkflowViewService ViewService
        {
            get 
            {
                if (null == this.viewService) 
                { 
                    this.viewService = (WorkflowViewService)this.context.Services.GetService();
                } 
                return this.viewService;
            }
        }
 
        void OnAttachedPropertiesServiceAvailable(AttachedPropertiesService attachedPropertiesService)
        { 
            this.validationStateProperty = new AttachedProperty() 
            {
                Getter = (modelItem) => GetValidationState(modelItem), 
                Name = "ValidationState",
                OwnerType = typeof(Activity)
            };
 
            attachedPropertiesService.AddProperty(this.validationStateProperty);
 
            this.validationMessageProperty = new AttachedProperty() 
            {
                Getter = (modelItem) => GetValidationMessage(modelItem), 
                Name = "ValidationMessage",
                OwnerType = typeof(Activity)
            };
 
            attachedPropertiesService.AddProperty(this.validationMessageProperty);
        } 
 
        ValidationState GetValidationState(ModelItem modelItem)
        { 
            ValidationState validationState = ValidationState.Valid;
            ValidationErrorState validationError = GetValidationError(modelItem);

            if (validationError != null) 
            {
                validationState = validationError.ValidationState; 
            } 
            return validationState;
        } 

        string GetValidationMessage(ModelItem modelItem)
        {
            string errorMessage = string.Empty; 
            ValidationErrorState validationError = GetValidationError(modelItem);
 
            if (validationError != null) 
            {
                if (validationError.ErrorMessages != null) 
                {
                    ValidationService.ErrorBuilder.Clear();
                    foreach (string message in validationError.ErrorMessages)
                    { 
                        ValidationService.ErrorBuilder.AppendLine(message);
                    } 
                    ValidationService.ErrorBuilder.Remove(errorBuilder.Length - 1, 1); 
                    errorMessage = ValidationService.ErrorBuilder.ToString();
                } 
            }
            return errorMessage;
        }
 
        ValidationErrorState GetValidationError(ModelItem modelItem)
        { 
            ValidationErrorState validationError = null; 
            Activity workflowElement = modelItem.GetCurrentValue() as Activity;
            if (workflowElement != null) 
            {
                this.ValidationErrors.TryGetValue(workflowElement, out validationError);
            }
            return validationError; 
        }
 
        void AddValidationError(ModelItem modelItem, ValidationErrorState error) 
        {
            Activity workflowElement = modelItem.GetCurrentValue() as Activity; 
            Fx.Assert(workflowElement != null, "modelItem current value can't be null");
            this.ValidationErrors.Add(workflowElement, error);
        }
 
        void OnModelServiceAvailable(ModelService modelService)
        { 
            if (modelService != null) 
            {
                this.modelService = modelService; 
            }
        }

        void OnModelTreeManagerAvailable(ModelTreeManager modelTreeManager) 
        {
            if (modelTreeManager != null) 
            { 
                this.modelTreeManager = modelTreeManager;
            } 
        }

        void OnErrorServiceAvailable(IValidationErrorService errorService)
        { 
            if (errorService != null)
            { 
                this.errorService = errorService; 
                if (this.isValidationDisabled)
                { 
                    this.errorService.ShowValidationErrors(new List { new ValidationErrorInfo(new ValidationError(SR.ValidationDisabledWarning, true)) });
                }
            }
        } 

        private void NotifyValidationPropertiesChanged(ModelItem modelItem) 
        { 
            // Notify an update to the attached properties
            this.validationStateProperty.NotifyPropertyChanged(modelItem); 
            this.validationMessageProperty.NotifyPropertyChanged(modelItem);
        }

        public void ValidateWorkflow() 
        {
            ValidateWorkflow(ValidationReason.Unknown); 
        } 

        internal void ValidateWorkflow(ValidationReason reason) 
        {
            if (!isValidationDisabled)
            {
                ValidateActivity(GetRootElement(), reason); 
            }
        } 
 
        Activity GetRootElement()
        { 
            Activity rootElement = null;

            Fx.Assert(this.modelService != null, "ModelService is null."); // ModelService should not be null
 
            ModelItem rootItem = this.modelService.Root;
            object root = rootItem.GetCurrentValue(); 
            //special case handling for ActivityBuilder - its arguments must be converted to variables before validation. 
            //otherwise, there will be validation error for each argument referenced
            if (root is ActivityBuilder) 
            {
                var activityBuilder = (ActivityBuilder)root;
                this.BodyPlaceholder.Activities.Clear();
                this.BodyPlaceholder.Variables.Clear(); 
                if (activityBuilder.Implementation != null)
                { 
                    this.BodyPlaceholder.Activities.Add(activityBuilder.Implementation); 
                    VisualBasic.SetSettings(this.BodyPlaceholder, VisualBasic.GetSettings(root));
                } 
                ActivityBuilderHelper.GetVariables(activityBuilder).ForEach(variable => this.BodyPlaceholder.Variables.Add(variable));
                rootElement = this.BodyPlaceholder;
            }
            else 
            {
                rootElement = rootItem.GetRootActivity(); 
            } 

            return rootElement; 
        }

        // Validate the imported WF element (and thus all its children)
        void ValidateActivity(Activity workflowElement, ValidationReason reason) 
        {
            if (workflowElement != null) 
            { 
                Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(
                    () => 
                    {
                        DesignerPerfEventProvider perfProvider = this.context.Services.GetService();
                        perfProvider.WorkflowDesignerValidationStart();
                        ValidationResults results = null; 
                        ValidationErrorInfo validationErrorInfo = null;
 
                        try 
                        {
                            results = ActivityValidationServices.Validate(workflowElement, this.settings); 
                        }
                        catch (System.Exception e)
                        {
                            ModelItem rootModelItem = this.modelService.Root; 
                            Activity rootActivity = rootModelItem.GetRootActivity();
 
                            if (rootActivity != null) 
                            {
                                // We dont want any crash propagating from here as it causes VS to crash. 
                                if (!this.ValidationErrors.ContainsKey(rootActivity))
                                {
                                    ValidationErrorState validationError = new ValidationErrorState(new List(), ValidationState.Error);
                                    this.ValidationErrors.Add(rootActivity, validationError); 
                                }
                                else 
                                { 
                                    this.ValidationErrors[rootActivity].ValidationState = ValidationState.Error;
                                } 

                                this.ValidationErrors[rootActivity].ErrorMessages.Add(e.ToString());

                                // Notify an update to the attached properties 
                                this.NotifyValidationPropertiesChanged(rootModelItem);
                            } 
 
                            validationErrorInfo = new ValidationErrorInfo(e.ToString());
                        } 

                        List validationErrors = null;
                        if (results != null)
                        { 
                            validationErrors = new List(results.Errors);
                            validationErrors.AddRange(results.Warnings); 
                        } 

                        bool needsToMarkValidationErrors = false; 
                        if (validationErrors != null)
                        {
                            needsToMarkValidationErrors = MarkErrors(validationErrors, reason);
                        } 

                        if (this.errorService != null) // Error service could be null if no implementation has been provided 
                        { 
                            List errors = new List();
 
                            if (validationErrors != null && needsToMarkValidationErrors)
                            {
                                foreach (ValidationError validationError in validationErrors)
                                { 
                                    ValidationErrorInfo error = new ValidationErrorInfo(validationError);
                                    errors.Add(error); 
                                } 
                            }
 
                            if (validationErrorInfo != null)
                            {
                                errors.Add(validationErrorInfo);
                            } 

                            this.errorService.ShowValidationErrors(errors); 
                        } 
                        perfProvider.WorkflowDesignerValidationEnd();
                        this.OnValidationCompleted(); 
                    }));
            }
        }
 
        protected virtual void OnValidationCompleted()
        { 
            if (this.ValidationCompleted != null) 
            {
                this.ValidationCompleted(this, new EventArgs()); 
            }
        }

        //  Find model item and properly create it if necessary. 
        ModelItem FindModelItem(Activity element)
        { 
            List parentChain = GetParentChain(element); 
            Fx.Assert(parentChain != null, "Cannot find parent chain for " + element.DisplayName);
 
            Fx.Assert(this.modelTreeManager != null, "ModelTreeManager is null."); // ModelTreeManager should not be null
            ModelItem lowestModelItem = null;
            foreach (Activity parent in parentChain)
            { 
                lowestModelItem = this.modelTreeManager.GetModelItem(parent);
                if (lowestModelItem != null) 
                { 
                    break;
                } 
            }
            Fx.Assert(lowestModelItem != null, "Cannot find lowest model item for parents of " + element.DisplayName); // Root ModelItem should not be null at this point

            // Walk the tree to create all the model items until it found the model item for the element. 
            return this.modelTreeManager.FindFirst(lowestModelItem, (modelItem) => (modelItem.GetCurrentValue() == element));
        } 
 
        internal static Activity GetParent(Activity childActivity)
        { 
            // Obtaining the parent from childActivity using (private) reflection.
            if (parentPropertyInfo == null)
            {
                parentPropertyInfo = typeof(Activity).GetProperty("Parent", BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic); 
            }
            Fx.Assert(parentPropertyInfo != null, "Activity.Parent is not defined"); 
            return parentPropertyInfo.GetValue(childActivity, null) as Activity; 
        }
 
        // Get the parent chain of this activity.
        // Can't use GetParentChain activity because it can only be used in a Constraint.
        internal static List GetParentChain(Activity activity)
        { 
            List parentChain = new List();
            while (activity != null) 
            { 
                activity = GetParent(activity);
                if (activity != null) 
                {
                    parentChain.Add(activity);
                }
            } 
            return parentChain;
        } 
 
        bool MarkErrors(ICollection errors, ValidationReason reason)
        { 
            // Clear the previous errors/warnings and update the visuals
            ClearErrors();
            Fx.Assert(this.modelTreeManager != null, "ModelTreeManager is null."); // ModelTreeManager should not be null
 
            if (HandleErrorsMarked(errors, reason))
            { 
                return false; 
            }
 
            // Iterate through the new violation list and mark errors/warnings
            foreach (ValidationError error in errors)
            {
                if (error.Source != null) 
                {
                    ModelItem modelItem = this.modelTreeManager.GetModelItem(error.Source); 
                    if (modelItem != null) 
                    {
                        MarkError(error, modelItem); 
                    }
                    else
                    {
                        MarkUnmappedValidationError(error); 
                    }
                } 
            } 

            return true; 
        }

        bool HandleErrorsMarked(ICollection errors, ValidationReason reason)
        { 
            if (this.ErrorsMarked != null)
            { 
                ErrorsMarkedEventArgs arg = new ErrorsMarkedEventArgs (errors, reason, this.modelTreeManager, this.context); 
                this.ErrorsMarked(this, arg);
                return arg.Handled; 
            }

            return false;
        } 

        void MarkUnmappedValidationError(ValidationError validationError) 
        { 
            Activity errorSource = validationError.Source;
            AddUnmappedValidationError(errorSource, validationError); 

            // Mark errorSource's ancestors.
            foreach (Activity parent in GetParentChain(errorSource))
            { 
                ModelItem modelItem = this.modelTreeManager.GetModelItem(parent);
                if (modelItem == null) 
                { 
                    AddUnmappedValidationError(parent, validationError);
                } 
                else
                {   // This parent already have a model item.
                    // Then MarkError will propagate to all its ancestors' ModelItem.
                    MarkParents(modelItem, validationError.IsWarning ? ValidationState.Warning : ValidationState.Error); 
                    break;
                } 
            } 
        }
 
        void AddUnmappedValidationError(Activity activity, ValidationError validationError)
        {
            ValidationErrorState currentError;
            if (!this.ValidationErrors.TryGetValue(activity, out currentError)) 
            {
                currentError = new ValidationErrorState(new List(), ValidationState.Valid); 
                this.ValidationErrors.Add(activity, currentError); 
            }
            MergeValidationError(currentError, validationError); 
        }

        static void MergeValidationError(ValidationErrorState originalError, ValidationError newError)
        { 
            if (originalError.ValidationState == ValidationState.ChildInvalid)
            { 
                // If original error is due to child's issue, clear the error list, 
                // as we don't care about its child's issues anymore and want to add its own issues.
                originalError.ErrorMessages.Clear(); 
            }

            ValidationState errorState = newError.IsWarning ? ValidationState.Warning : ValidationState.Error;
            if (originalError.ValidationState < errorState) 
            {  // Promote to the higher level of violation.
                originalError.ValidationState = errorState; 
            } 

            if (newError.IsWarning) 
            {
                originalError.ErrorMessages.Add(string.Format(CultureInfo.CurrentUICulture, "{0}{1}", SR.WarningPrefix, newError.Message));
            }
            else 
            {
                originalError.ErrorMessages.Add(newError.Message); 
            } 
        }
 
        // Mark this error to corresponding ModelItem.
        // Mark all its ancestors accordingly.
        void MarkError(ValidationError error, ModelItem modelItem)
        { 
            ValidationErrorState currentError = this.GetValidationError(modelItem);
            if (currentError == null) 
            { 
                currentError = new ValidationErrorState(new List(), ValidationState.Valid);
                this.AddValidationError(modelItem, currentError); 
            }

            MergeValidationError(currentError, error);
 
            MarkParents(modelItem.Parent, this.GetValidationState(modelItem));
 
            // Notify an update to the attached properties 
            NotifyValidationPropertiesChanged(modelItem);
        } 

        void MarkParents(ModelItem parentModelItem, ValidationState childValidationState)
        {
            List childErrors = new List(); 
            childErrors.Add(SR.ChildValidationError);
            Activity parentActivity; 
            while (parentModelItem != null) 
            {
                parentActivity = parentModelItem.GetCurrentValue() as Activity; 
                if (parentActivity != null)
                {
                    bool updateAttachedProperties = false;
                    ValidationErrorState validationError; 
                    if (!this.ValidationErrors.TryGetValue(parentActivity, out validationError))
                    { 
                        validationError = new ValidationErrorState(childErrors, ValidationState.ChildInvalid); 
                        this.AddValidationError(parentModelItem, validationError);
                        updateAttachedProperties = true; 
                    }

                    if (validationError.ValidationState < childValidationState)
                    { 
                        validationError.ValidationState = childValidationState;
                        updateAttachedProperties = true; 
                    } 

                    // Notify and update to the attached properties 
                    if (updateAttachedProperties)
                    {
                        NotifyValidationPropertiesChanged(parentModelItem);
                    } 
                }
                parentModelItem = parentModelItem.Parent; 
            } 
        }
 

        void ClearErrors()
        {
            // Copy over the previously marked model items before you clear the dictionaries 
            Activity[] oldErrorList = new Activity[this.ValidationErrors.Count];
            this.ValidationErrors.Keys.CopyTo(oldErrorList, 0); 
 
            this.ValidationErrors.Clear();
 
            // Iterate through the previously marked model items and notify an update to the attached properties
            ModelItem modelItem;
            foreach (Activity workflowElement in oldErrorList)
            { 
                modelItem = this.modelTreeManager.GetModelItem(workflowElement);
                if (modelItem != null) 
                { 
                    NotifyValidationPropertiesChanged(modelItem);
                } 
            }
        }

 
        public void NavigateToError(string id)
        { 
            ModelItem modelItem = null; 

            if (id != null) 
            {
                Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(
                () =>
                { 
                    Activity errorElement = ActivityValidationServices.Resolve(GetRootElement(), id);
                    if (errorElement != null) 
                    { 
                        Fx.Assert(this.modelTreeManager != null, "ModelTreeManager is null."); // ModelTreeManager should not be null
                        modelItem = this.modelTreeManager.GetModelItem(errorElement) ?? this.FindModelItem(errorElement); 

                        if (modelItem != null)
                        {
                            // For a VB Expression, need to focus to its parent instead. 
                            Activity activity = modelItem.GetCurrentValue() as Activity;
                            if (activity != null && (activity.IsVisualBasicExpression())) 
                            { 
                                ModelItem parent = modelItem.Parent;
                                while (parent != null && this.ViewService.GetDesignerType(parent.ItemType) == null) 
                                {
                                    parent = parent.Parent;
                                }
                                if (parent != null) 
                                {
                                    modelItem = parent; 
                                } 
                            }
                            modelItem.Focus(); 
                        }
                    }
                }));
            } 
        }
 
        // Properties 

        Dictionary ValidationErrors 
        {
            get
            {
                if (this.validationErrors == null) 
                {
                    this.validationErrors = new Dictionary(); 
                } 
                return this.validationErrors;
            } 
        }

        internal AttachedProperty ValidationStateProperty
        { 
            get
            { 
                return this.validationStateProperty; 
            }
        } 

        internal AttachedProperty ValidationMessageProperty
        {
            get 
            {
                return this.validationMessageProperty; 
            } 
        }
 
        class ValidationErrorState
        {
            internal ValidationErrorState(List errorMessages, ValidationState validationState)
            { 
                this.ErrorMessages = errorMessages;
                this.ValidationState = validationState; 
            } 

            internal List ErrorMessages { get; set; } 
            internal ValidationState ValidationState { get; set; }
        }
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK