Click here to Skip to main content
15,885,366 members
Articles / Desktop Programming / WPF

Building WPF Applications with Self-Tracking Entity Generator and Visual Studio 2012 - Project Setup

Rate me:
Please Sign up or sign in to vote.
5.00/5 (14 votes)
17 Mar 2013CPOL8 min read 68.5K   3.5K   44  
This article describes the project setup of building a WPF sample application with Self-Tracking Entity Generator and Visual Studio 2012.
//-----------------------------------------------------------------------
// <copyright company="Microsoft">
//      (c) Copyright Microsoft Corporation.
//      This source is subject to the Microsoft Public License (Ms-PL).
//      Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
//      All other rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Common;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using resources = System.Windows.Controls.Data.Input.Resources;

namespace System.Windows.Controls
{
    /// <summary>
    /// Displays a summary of validation errors on a form.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    [TemplatePart(Name = PART_SummaryListBox, Type = typeof(ListBox))]
    [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)]
    [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)]
    [TemplateVisualState(Name = VisualStates.StateEmpty, GroupName = VisualStates.GroupValidation)]
    [TemplateVisualState(Name = VisualStates.StateHasErrors, GroupName = VisualStates.GroupValidation)]
    [StyleTypedProperty(Property = "SummaryListBoxStyle", StyleTargetType = typeof(ListBox))]
    [StyleTypedProperty(Property = "ErrorStyle", StyleTargetType = typeof(ListBoxItem))]
    public class ValidationSummary : Control
    {
        #region Static Fields and Constants

        private const string PART_SummaryListBox = "SummaryListBox";
        private const string PART_HeaderContentControl = "HeaderContentControl";

        #endregion Static Fields and Constants

        #region Member Fields

        private ValidationSummaryItemSource _currentValidationSummaryItemSource;
        private ValidationItemCollection _displayedErrors;
        private ValidationItemCollection _errors;
        private ListBox _errorsListBox;
        private ContentControl _headerContentControl;
        private bool _initialized;
        private FrameworkElement _registeredParent;
        private Dictionary<string, List<ValidationSummaryItem>> _validationSummaryItemDictionary;

        #endregion Member Fields

        #region Events

        /// <summary>
        /// Event triggered when an Error is clicked on.
        /// </summary>
        public event EventHandler<FocusingInvalidControlEventArgs> FocusingInvalidControl;

        /// <summary>
        /// Event triggered when the selected error has changed.
        /// </summary>
        public event EventHandler<SelectionChangedEventArgs> SelectionChanged;

        #endregion Events

        #region	Constructors

        /// <summary>
        /// Initializes a new instance of the ValidationSummary class.
        /// </summary>
        public ValidationSummary()
        {
            this.DefaultStyleKey = typeof(ValidationSummary);
            this._errors = new ValidationItemCollection();
            this._validationSummaryItemDictionary = new Dictionary<string, List<ValidationSummaryItem>>();
            this._displayedErrors = new ValidationItemCollection();
            this._errors.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(this.Errors_CollectionChanged);
            this.Loaded += new RoutedEventHandler(this.ValidationSummary_Loaded);
            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(this.ValidationSummary_IsEnabledChanged);
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                this.Errors.Add(new ValidationSummaryItem(resources.ValidationSummarySampleError, typeof(ValidationSummaryItem).Name, ValidationSummaryItemType.ObjectError, null, null));
                this.Errors.Add(new ValidationSummaryItem(resources.ValidationSummarySampleError, typeof(ValidationSummaryItem).Name, ValidationSummaryItemType.ObjectError, null, null));
                this.Errors.Add(new ValidationSummaryItem(resources.ValidationSummarySampleError, typeof(ValidationSummaryItem).Name, ValidationSummaryItemType.ObjectError, null, null));
            }
        }

        #endregion Constructors

        #region Attached Properties

        #region ShowErrorsInSummary

        /// <summary>
        /// Gets or sets a value indicating whether the field errors belonging to the input control should be shown in the ValidationSummary. 
        /// Errors are added to the DisplayedErrors list depending on this flag.  The base Errors list, however, will always contain all
        /// the errors.
        /// </summary>
        public static readonly DependencyProperty ShowErrorsInSummaryProperty = DependencyProperty.RegisterAttached(
            "ShowErrorsInSummary",
            typeof(bool),
            typeof(ValidationSummary),
            new PropertyMetadata(true, OnShowErrorsInSummaryPropertyChanged));

        /// <summary>
        /// Gets the ShowErrorsInSummary property of the specified DependencyObject.
        /// </summary>
        /// <param name="inputControl">The input control to get the ShowErrorsInSummary property from.</param>
        /// <returns>The value indicating whether errors on the input control should be shown.</returns>
        public static bool GetShowErrorsInSummary(DependencyObject inputControl)
        {
            if (inputControl == null)
            {
                throw new ArgumentNullException("inputControl");
            }
            return (bool)inputControl.GetValue(ShowErrorsInSummaryProperty);
        }

        /// <summary>
        /// Sets the ShowErrorsInSummary property of the specified DependencyObject.
        /// </summary>
        /// <param name="inputControl">The input control with which to associate the specified dependency property.</param>
        /// <param name="value">The value indicating whether errors on the input control should be shown.</param>
        public static void SetShowErrorsInSummary(DependencyObject inputControl, bool value)
        {
            if (inputControl == null)
            {
                throw new ArgumentNullException("inputControl");
            }
            inputControl.SetValue(ShowErrorsInSummaryProperty, value);
        }

        private static void OnShowErrorsInSummaryPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement page = (Application.Current != null) ? Application.Current.MainWindow as FrameworkElement : null;
            if (page != null)
            {
                UpdateDisplayedErrorsOnAllValidationSummaries(page);
            }
        }

        #endregion ShowErrorsInSummary

        #endregion Attached Properties

        #region Dependency Properties

        #region ErrorStyle

        /// <summary>
        /// Identifies the ErrorStyle dependency property
        /// </summary>
        public static readonly DependencyProperty ErrorStyleProperty =
            DependencyProperty.Register(
            "ErrorStyle",
            typeof(Style),
            typeof(ValidationSummary),
            null);

        /// <summary>
        /// Gets or sets the style used for the error's item container.
        /// </summary>
        public Style ErrorStyle
        {
            get { return GetValue(ErrorStyleProperty) as Style; }
            set { SetValue(ErrorStyleProperty, value); }
        }

        #endregion ErrorStyle

        #region Filter

        /// <summary>
        /// Identifies the Filter dependency property
        /// </summary>
        public static readonly DependencyProperty FilterProperty =
            DependencyProperty.Register(
            "Filter",
            typeof(ValidationSummaryFilters),
            typeof(ValidationSummary),
            new PropertyMetadata(ValidationSummaryFilters.All, OnFilterPropertyChanged));

        /// <summary>
        /// Gets or sets a value that indicates which types of errors are displayed.
        /// </summary>
        public ValidationSummaryFilters Filter
        {
            get { return (ValidationSummaryFilters)GetValue(FilterProperty); }
            set { SetValue(FilterProperty, value); }
        }

        private static void OnFilterPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ValidationSummary vs = d as ValidationSummary;
            if (vs != null)
            {
                vs.UpdateDisplayedErrors();
            }
        }

        #endregion Filter

        #region FocusControlsOnClick

        /// <summary>
        /// Identifies the FocusControlsOnClick dependency property.
        /// </summary>
        public static readonly DependencyProperty FocusControlsOnClickProperty =
            DependencyProperty.Register(
            "FocusControlsOnClick",
            typeof(bool),
            typeof(ValidationSummary),
            new PropertyMetadata(true, null));

        /// <summary>
        /// Gets or sets a value that indicates whether focus is set on the 
        /// input control when an error message is clicked.
        /// </summary>
        public bool FocusControlsOnClick
        {
            get { return (bool)GetValue(FocusControlsOnClickProperty); }
            set { SetValue(FocusControlsOnClickProperty, value); }
        }

        #endregion FocusControlsOnClick

        #region HasErrors

        /// <summary>
        /// Identifies the HasErrors dependency property
        /// </summary>
        public static readonly DependencyProperty HasErrorsProperty =
            DependencyProperty.Register(
            "HasErrors",
            typeof(bool),
            typeof(ValidationSummary),
            new PropertyMetadata(false, OnHasErrorsPropertyChanged));

        /// <summary>
        ///   Gets or sets a value that indicates whether the <see cref="T:System.Windows.Controls.ValidationSummary" /> has errors. 
        /// </summary>
        public bool HasErrors
        {
            get { return (bool)GetValue(HasErrorsProperty); }
            internal set { this.SetValueNoCallback(HasErrorsProperty, value); }
        }

        private static void OnHasErrorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ValidationSummary vs = d as ValidationSummary;
            if (vs != null && !vs.AreHandlersSuspended())
            {
                vs.SetValueNoCallback(ValidationSummary.HasErrorsProperty, e.OldValue);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, resources.UnderlyingPropertyIsReadOnly, "HasErrors"));
            }
        }

        #endregion HasErrors

        #region HasDisplayedErrors

        /// <summary>
        /// Identifies the HasDisplayedErrors dependency property
        /// </summary>
        public static readonly DependencyProperty HasDisplayedErrorsProperty =
            DependencyProperty.Register(
            "HasDisplayedErrors",
            typeof(bool),
            typeof(ValidationSummary),
            new PropertyMetadata(false, OnHasDisplayedErrorsPropertyChanged));

        /// <summary>
        ///   Gets or sets a value that indicates whether the 
        ///   <see cref="T:System.Windows.Controls.ValidationSummary" /> has displayed errors. 
        /// </summary>
        public bool HasDisplayedErrors
        {
            get { return (bool)GetValue(HasDisplayedErrorsProperty); }
            internal set { this.SetValueNoCallback(HasDisplayedErrorsProperty, value); }
        }

        private static void OnHasDisplayedErrorsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ValidationSummary vs = d as ValidationSummary;
            if (vs != null && !vs.AreHandlersSuspended())
            {
                vs.SetValueNoCallback(ValidationSummary.HasDisplayedErrorsProperty, e.OldValue);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, resources.UnderlyingPropertyIsReadOnly, "HasDisplayedErrors"));
            }
        }

        #endregion HasDisplayedErrors

        #region Header

        /// <summary>
        /// Identifies the Header dependency property
        /// </summary>
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register(
            "Header",
            typeof(object),
            typeof(ValidationSummary),
            new PropertyMetadata(OnHasHeaderPropertyChanged));

        /// <summary>
        /// Gets or sets the content of the <see cref="T:System.Windows.Controls.ValidationSummary" /> header. 
        /// </summary>
        public object Header
        {
            get { return GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        private static void OnHasHeaderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ValidationSummary vs = d as ValidationSummary;
            if (vs != null)
            {
                vs.UpdateHeaderText();
            }
        }

        #endregion Header

        #region HeaderTemplate

        /// <summary>
        /// Identifies the HeaderTemplate dependency property
        /// </summary>
        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register(
            "HeaderTemplate",
            typeof(DataTemplate),
            typeof(ValidationSummary),
            null);

        /// <summary>
        /// Gets or sets the template that is used to display the content of the header.
        /// </summary>
        public DataTemplate HeaderTemplate
        {
            get { return GetValue(HeaderTemplateProperty) as DataTemplate; }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        #endregion HeaderTemplate

        #region SummaryListBoxStyle

        /// <summary>
        /// Identifies the SummaryListBoxStyle dependency property.
        /// </summary>
        public static readonly DependencyProperty SummaryListBoxStyleProperty =
            DependencyProperty.Register(
            "SummaryListBoxStyle",
            typeof(Style),
            typeof(ValidationSummary),
            null);

        /// <summary>
        /// Gets or sets the style applied to the <see cref="T:System.Windows.Controls.ListBox" /> that displays the errors. 
        /// </summary>
        public Style SummaryListBoxStyle
        {
            get { return GetValue(SummaryListBoxStyleProperty) as Style; }
            set { SetValue(SummaryListBoxStyleProperty, value); }
        }

        #endregion SummaryListBoxStyle

        #region Target

        /// <summary>
        /// Identifies the Target dependency property.
        /// </summary>
        public static readonly DependencyProperty TargetProperty =
            DependencyProperty.Register(
            "Target",
            typeof(UIElement),
            typeof(ValidationSummary),
            new PropertyMetadata(OnTargetPropertyChanged));

        /// <summary>
        /// Gets or sets the <see cref="T:System.Windows.UIElement" /> for 
        /// which validation errors will be displayed in the summary. 
        /// </summary>
        public UIElement Target
        {
            get { return GetValue(TargetProperty) as UIElement; }
            set { SetValue(TargetProperty, value); }
        }

        private static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement oldElement = e.OldValue as FrameworkElement;
            ValidationSummary vs = d as ValidationSummary;
            EventHandler<ValidationErrorEventArgs> handler = new EventHandler<ValidationErrorEventArgs>(vs.Target_BindingValidationError);
            if (vs._registeredParent != null)
            {
                Validation.RemoveErrorHandler(vs._registeredParent, handler);
                //vs._registeredParent.BindingValidationError -= handler;
                vs._registeredParent = null;
            }
            if (oldElement != null)
            {
                Validation.RemoveErrorHandler(oldElement, handler);
                //oldElement.BindingValidationError -= handler;
            }
            FrameworkElement newElement = e.NewValue as FrameworkElement;
            if (newElement != null)
            {
                Validation.AddErrorHandler(newElement, handler);
                //newElement.BindingValidationError += handler;
            }

            // Clear the old property binding errors
            vs._errors.ClearErrors(ValidationSummaryItemType.PropertyError);
            vs.UpdateDisplayedErrors();
        }

        #endregion Target

        #endregion Dependency Properties

        #region Properties

        /// <summary>
        /// Gets the collection of errors.
        /// </summary>
        public ObservableCollection<ValidationSummaryItem> Errors
        {
            get { return this._errors; }
        }

        /// <summary>
        /// Gets the collection of errors that are displayed after 
        /// the <see cref="P:System.Windows.Controls.ValidationSummary.Filter" /> is applied. 
        /// </summary>
        public ReadOnlyObservableCollection<ValidationSummaryItem> DisplayedErrors
        {
            get { return new ReadOnlyObservableCollection<ValidationSummaryItem>(this._displayedErrors); }
        }

        /// <summary>
        /// Gets a value indicating whether the ValidationSummary is initialized.
        /// </summary>
        internal new bool Initialized
        {
            get { return this._initialized; }
        }

        /// <summary>
        /// Gets the ErrorsListBox template part
        /// </summary>
        internal ListBox ErrorsListBoxInternal
        {
            get { return this._errorsListBox; }
        }

        /// <summary>
        /// Gets the HeaderContentControl template part
        /// </summary>
        internal ContentControl HeaderContentControlInternal
        {
            get { return this._headerContentControl; }
        }

        #endregion Properties

        #region	Methods

        #region Static Methods

        /// <summary>
        /// Compare ValidationSummaryItems for display in the ValidationSummary
        /// </summary>
        /// <param name="x">The first reference used for comparison.</param>
        /// <param name="y">The second reference used for comparison.</param>
        /// <returns>The result of the comparison check between the two references.</returns>
        internal static int CompareValidationSummaryItems(ValidationSummaryItem x, ValidationSummaryItem y)
        {
            int returnVal;
            if (!ReferencesAreValid(x, y, out returnVal))
            {
                // Do a null comparison check if one (or both) are null
                return returnVal;
            }

            // Compare ErrorSource
            if (TryCompareReferences(x.ItemType, y.ItemType, out returnVal))
            {
                return returnVal;
            }

            // Compare Control
            Control controlX = x.Sources.Count > 0 ? x.Sources[0].Control : null;
            Control controlY = y.Sources.Count > 0 ? y.Sources[0].Control : null;

            if (controlX != controlY)
            {
                if (!ReferencesAreValid(controlX, controlY, out returnVal))
                {
                    // Do a null comparison check if one is null
                    return returnVal;
                }

                // Both are controls
                if (controlX.TabIndex != controlY.TabIndex)
                {
                    // Sort by TabIndex
                    return controlX.TabIndex.CompareTo(controlY.TabIndex);
                }

                // Both share the same parent, sort by index
                returnVal = SortByVisualTreeOrdering(controlX, controlY);
                if (returnVal != 0)
                {
                    return returnVal;
                }

                // TabIndexes and ordering are the same, move to next check
                if (TryCompareReferences(controlX.Name, controlY.Name, out returnVal))
                {
                    return returnVal;
                }
            }

            // If we reached this point, we could not compare by Control, TabIndex, nor Name.  
            // Compare by MessageHeader
            if (TryCompareReferences(x.MessageHeader, y.MessageHeader, out returnVal))
            {
                return returnVal;
            }

            // Compare by ErrorMessage
            TryCompareReferences(x.Message, y.Message, out returnVal);
            return returnVal;
        }

        private static int FindMatchingErrorSource(IList<ValidationSummaryItemSource> sources, ValidationSummaryItemSource sourceToFind)
        {
            if (sources != null)
            {
                for (int i = 0; i < sources.Count; i++)
                {
                    if (sources[i].Equals(sourceToFind))
                    {
                        return i;
                    }
                }
            }
            return -1;
        }

        /// <summary>
        /// Try to compare two references, but only if they they are comparable
        /// </summary>
        /// <param name="x">The first reference to compare.</param>
        /// <param name="y">The second reference to compare.</param>
        /// <param name="returnVal">The comparison value.</param>
        /// <returns>Returns true if the two references were able to be compared.</returns>
        private static bool TryCompareReferences(object x, object y, out int returnVal)
        {
            // If the two references are equal, then they should not be used for comparison purposes (in this context)
            // and we should try to use the next set of candidate properties.
            if ((x == null && y == null) || (x != null && x.Equals(y)))
            {
                returnVal = 0;
                return false;
            }

            // Do a reference level comparison, such as if one field is null whereas the other is not null.
            if (!ReferencesAreValid(x, y, out returnVal))
            {
                return true;
            }

            // If both references are valid (non null), try a standard comparison.
            IComparable comparableX = x as IComparable;
            IComparable comparableY = y as IComparable;
            if (comparableX != null && comparableY != null)
            {
                returnVal = comparableX.CompareTo(comparableY);
                return true;
            }

            // Could not compare
            returnVal = 0;
            return false;
        }

        /// <summary>
        /// Perform a null comparison check if one (or both) are null
        /// </summary>
        /// <param name="x">The first reference to compare.</param>
        /// <param name="y">The second reference to compare.</param>
        /// <param name="val">The comparison value.</param>
        /// <returns>Returns true if both references are non-null</returns>
        private static bool ReferencesAreValid(object x, object y, out int val)
        {
            if (x == null)
            {
                val = (y == null) ? 0 : -1;
                return false;
            }
            else if (y == null)
            {
                val = 1;
                return false;
            }
            val = 0;
            return true;
        }

        /// <summary>
        /// When one of the input controls has its ShowErrorsInSummary property changed, we have to go through all the ValidationSummaries on the page and update them
        /// </summary>
        /// <param name="parent">The parent ValidationSummary</param>
        private static void UpdateDisplayedErrorsOnAllValidationSummaries(DependencyObject parent)
        {
            if (parent == null)
            {
                return;
            }

            ValidationSummary vs = parent as ValidationSummary;
            if (vs != null)
            {
                vs.UpdateDisplayedErrors();
                return;
            }

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(parent, i);
                UpdateDisplayedErrorsOnAllValidationSummaries(child);
            }
        }

        #endregion Static Methods

        #region Public Methods

        /// <summary>
        /// When the template is applied, this loads all the template parts
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            MouseButtonEventHandler mouseUpHandler = new MouseButtonEventHandler(this.ErrorsListBox_MouseLeftButtonUp);
            KeyEventHandler keyDownHandler = new KeyEventHandler(this.ErrorsListBox_KeyDown);
            SelectionChangedEventHandler selectionChangedHandler = new SelectionChangedEventHandler(this.ErrorsListBox_SelectionChanged);

            if (this._errorsListBox != null)
            {
                // If the ErrorsListBox was already set (due to multiple calls to OnApplyTemplate), unload the handlers first.
                this._errorsListBox.MouseLeftButtonUp -= mouseUpHandler;
                this._errorsListBox.KeyDown -= keyDownHandler;
                this._errorsListBox.SelectionChanged -= selectionChangedHandler;
            }

            this._errorsListBox = GetTemplateChild(PART_SummaryListBox) as ListBox;
            if (this._errorsListBox != null)
            {
                this._errorsListBox.MouseLeftButtonUp += mouseUpHandler;
                this._errorsListBox.KeyDown += keyDownHandler;
                this._errorsListBox.ItemsSource = this.DisplayedErrors;
                this._errorsListBox.SelectionChanged += selectionChangedHandler;
            }
            this._headerContentControl = GetTemplateChild(PART_HeaderContentControl) as ContentControl;
            this.UpdateDisplayedErrors();
            this.UpdateCommonState(false);
            this.UpdateValidationState(false);
        }

        /// <summary>
        /// OnErrorClicked is invoked when an error in the ValidationSummary is clicked, via either the mouse or keyboard.
        /// </summary>
        /// <param name="e">The FocusingInvalidControlEventArgs for the event.</param>
        protected virtual void OnFocusingInvalidControl(FocusingInvalidControlEventArgs e)
        {
            EventHandler<FocusingInvalidControlEventArgs> handler = this.FocusingInvalidControl;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion Public Methods

        #region Protected Methods

        /// <summary>
        /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
        /// </summary>
        /// <returns>The AutomationPeer associated with this ValidationSummary.</returns>
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new ValidationSummaryAutomationPeer(this);
        }

        #endregion Protected Methods

        #region Internal Methods

        /// <summary>
        /// Simulate a click
        /// </summary>
        internal void ExecuteClickInternal()
        {
            this.ExecuteClick(this.ErrorsListBoxInternal);
        }

        internal string GetHeaderString()
        {
            string errorString = this._displayedErrors.Count == 1 ? resources.ValidationSummaryHeaderError : String.Format(CultureInfo.InvariantCulture, resources.ValidationSummaryHeaderErrors, this._displayedErrors.Count);
            return errorString;
        }

        /// <summary>
        /// Sort two dependency objects based on their relative positions within the VisualTree.  
        /// </summary>
        /// <param name="controlX">The first control being compared.</param>
        /// <param name="controlY">The second value being compared.</param>
        /// <returns>The sort value indicating the ordering between the two controls.</returns>
        internal static int SortByVisualTreeOrdering(DependencyObject controlX, DependencyObject controlY)
        {
            if (controlX == null || controlY == null || controlX == controlY)
            {
                return 0;
            }
            List<DependencyObject> parentChain = new List<DependencyObject>();
            DependencyObject node = controlX;
            parentChain.Add(node);
            while ((node = VisualTreeHelper.GetParent(node)) != null)
            {
                parentChain.Add(node);
            }

            node = controlY;
            DependencyObject lastNodeY = node;
            while ((node = VisualTreeHelper.GetParent(node)) != null)
            {
                int idxParentChain = parentChain.IndexOf(node);
                if (idxParentChain == 0)
                {
                    // X itself is the ancestor
                    return -1;
                }
                else if (idxParentChain > 0)
                {
                    // Common ancestor found
                    DependencyObject lastNodeX = parentChain[idxParentChain - 1];
                    if (lastNodeX == null || lastNodeY == null)
                    {
                        return 0;
                    }
                    int numChildren = VisualTreeHelper.GetChildrenCount(node);
                    for (int i = 0; i < numChildren; i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(node, i);
                        if (child == lastNodeY)
                        {
                            // Y appears first.  This is checked first, as it has precedence when xLastNode == yLastNode
                            return 1;
                        }
                        if (child == lastNodeX)
                        {
                            return -1;
                        }
                    }
                }
                lastNodeY = node;
            }
            return 0;
        }

        #endregion Internal Methods

        #region Private Methods

        private void ErrorsListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                this.ExecuteClick(sender);
            }
        }

        private void ErrorsListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.ExecuteClick(sender);
        }

        private void ErrorsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            EventHandler<SelectionChangedEventArgs> handler = this.SelectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void Errors_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (ValidationSummaryItem vsi in e.OldItems)
                {
                    if (vsi != null)
                    {
                        vsi.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(this.ValidationSummaryItem_PropertyChanged);
                    }
                }
            }
            if (e.NewItems != null)
            {
                foreach (ValidationSummaryItem vsi in e.NewItems)
                {
                    if (vsi != null)
                    {
                        vsi.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.ValidationSummaryItem_PropertyChanged);
                    }
                }
            }
            this.HasErrors = this._errors.Count > 0;
            this.UpdateDisplayedErrors();
        }

        private void ExecuteClick(object sender)
        {
            ListBox lb = sender as ListBox;
            if (lb != null)
            {
                ValidationSummaryItem vsi = lb.SelectedItem as ValidationSummaryItem;
                if (vsi != null && this.FocusControlsOnClick)
                {
                    int idx;
                    // Ensure the currently selected item source is valid
                    if (vsi.Sources.Count == 0)
                    {
                        // Clear the current ESI source if the ESI has none, such as when the ESI has changed
                        this._currentValidationSummaryItemSource = null;
                    }
                    else
                    {
                        // If the current ESI source is not part of the current set, select the first one by default.
                        idx = FindMatchingErrorSource(vsi.Sources, this._currentValidationSummaryItemSource);
                        if (idx < 0)
                        {
                            this._currentValidationSummaryItemSource = vsi.Sources[0];
                        }
                    }

                    // Raise the event
                    FocusingInvalidControlEventArgs e = new FocusingInvalidControlEventArgs(vsi, this._currentValidationSummaryItemSource);
                    this.OnFocusingInvalidControl(e);

                    // Raise the AutomationPeer event
                    ValidationSummaryAutomationPeer peer = ValidationSummaryAutomationPeer.FromElement(this) as ValidationSummaryAutomationPeer;
                    if (peer != null && AutomationPeer.ListenerExists(AutomationEvents.InvokePatternOnInvoked))
                    {
                        peer.RaiseAutomationEvent(AutomationEvents.InvokePatternOnInvoked);
                    }

                    // Focus the target, which will usually be the current ESI source or the overwritten one
                    if (!e.Handled && e.Target != null && e.Target.Control != null)
                    {
                        e.Target.Control.Focus();
                    }

                    // Update currently selected item, but only if there are multiple ESI sources.  
                    if (vsi.Sources.Count > 0)
                    {
                        idx = FindMatchingErrorSource(vsi.Sources, e.Target);
                        idx = idx < 0 ? 0 : ++idx % vsi.Sources.Count;
                        this._currentValidationSummaryItemSource = vsi.Sources[idx];
                    }
                }
            }
        }

        private void Target_BindingValidationError(object sender, ValidationErrorEventArgs e)
        {
            FrameworkElement inputControl = e.OriginalSource as FrameworkElement;
            if (e != null && e.Error != null && e.Error.ErrorContent != null && inputControl != null)
            {
                string message = e.Error.ErrorContent.ToString();
                string key = String.IsNullOrEmpty(inputControl.Name) ? inputControl.GetHashCode().ToString(CultureInfo.InvariantCulture) : inputControl.Name;
                key += message;

                switch (e.Action)
                {
                    case ValidationErrorEventAction.Added:
                        if (GetShowErrorsInSummary(inputControl))
                        {
                            // New error
                            string propertyName = null;
                            object entity;
                            BindingExpression be;
                            ValidationMetadata vmd = ValidationHelper.ParseMetadata(inputControl, false, out entity, out be);
                            if (vmd != null)
                            {
                                propertyName = vmd.Caption;
                            }
                            ValidationSummaryItem vsi = new ValidationSummaryItem(message, propertyName, ValidationSummaryItemType.PropertyError, new ValidationSummaryItemSource(propertyName, inputControl as Control), null);
                            this._errors.Add(vsi);
                            if (!this._validationSummaryItemDictionary.ContainsKey(key))
                            {
                                this._validationSummaryItemDictionary[key] = new List<ValidationSummaryItem>();
                                this._validationSummaryItemDictionary[key].Add(vsi);
                            }
                            else
                            {
                                this._validationSummaryItemDictionary[key].Add(vsi);
                            }
                        }
                        break;
                    case ValidationErrorEventAction.Removed:
                        if (this._validationSummaryItemDictionary.ContainsKey(key))
                        {
                            ValidationSummaryItem existingError = this._validationSummaryItemDictionary[key][0];
                            this._errors.Remove(existingError);
                            this._validationSummaryItemDictionary[key].Remove(existingError);
                            if (this._validationSummaryItemDictionary[key].Count == 0)
                            {
                                this._validationSummaryItemDictionary.Remove(key);
                            }
                        }
                        break;
                }
            }
        }

        private void UpdateDisplayedErrors()
        {
            System.Collections.Generic.List<ValidationSummaryItem> newErrors = new System.Collections.Generic.List<ValidationSummaryItem>();
            Debug.Assert(this.Errors != null, "ValidationSummary.Errors should not be null");
            foreach (ValidationSummaryItem vsi in this.Errors)
            {
                if (vsi != null &&
                    ((vsi.ItemType == ValidationSummaryItemType.ObjectError && (this.Filter & ValidationSummaryFilters.ObjectErrors) != 0) ||
                    (vsi.ItemType == ValidationSummaryItemType.PropertyError && (this.Filter & ValidationSummaryFilters.PropertyErrors) != 0)))
                {
                    newErrors.Add(vsi);
                }
            }
            newErrors.Sort(CompareValidationSummaryItems);
            this._displayedErrors.Clear();
            foreach (ValidationSummaryItem vsi in newErrors)
            {
                this._displayedErrors.Add(vsi);
            }
            this.UpdateValidationState(true);
            this.UpdateHeaderText();
        }

        private void UpdateHeaderText()
        {
            if (this._headerContentControl != null)
            {
                if (this.Header != null)
                {
                    this._headerContentControl.Content = this.Header;
                }
                else
                {
                    this._headerContentControl.Content = this.GetHeaderString();
                }
            }
        }

        private void UpdateValidationState(bool useTransitions)
        {
            this.HasDisplayedErrors = this._displayedErrors.Count > 0;
            VisualStateManager.GoToState(this, this.HasDisplayedErrors ? VisualStates.StateHasErrors : VisualStates.StateEmpty, useTransitions);
        }

        private void UpdateCommonState(bool useTransitions)
        {
            if (this.IsEnabled)
            {
                VisualStateManager.GoToState(this, VisualStates.StateNormal, useTransitions);
            }
            else
            {
                VisualStateManager.GoToState(this, VisualStates.StateDisabled, useTransitions);
            }
        }

        private void ValidationSummary_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.Target == null && this._registeredParent == null)
            {
                this._registeredParent = VisualTreeHelper.GetParent(this) as FrameworkElement;
                if (this._registeredParent != null)
                {
                    Validation.AddErrorHandler(this._registeredParent, this.Target_BindingValidationError);
                    //this._registeredParent.BindingValidationError += new EventHandler<ValidationErrorEventArgs>(this.Target_BindingValidationError);
                }
            }
            this.Loaded -= new RoutedEventHandler(this.ValidationSummary_Loaded);
            this._initialized = true;
        }

        private void ValidationSummary_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.UpdateCommonState(true);
        }

        private void ValidationSummaryItem_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == ValidationSummaryItem.PROPERTYNAME_ITEMTYPE)
            {
                this.UpdateDisplayedErrors();
            }
        }

        #endregion Private Methods

        #endregion Methods
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
Weidong has been an information system professional since 1990. He has a Master's degree in Computer Science, and is currently a MCSD .NET

Comments and Discussions