Click here to Skip to main content
12,354,719 members (63,739 online)
Click here to Skip to main content

Stats

43.7K views
4.3K downloads
55 bookmarked
Posted

Building WPF Applications with Self-Tracking Entity Generator - Project Setup

, 20 Feb 2012 CPOL
This article describes the project setup of building a WPF sample application with Self-Tracking Entity Generator for WPF/Silverlight.
SchoolSample
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
Assemblies
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
ComponentModel.Composition.Initialization.Desktop
Microsoft
ComponentModel
Composition
Hosting
Internal
Properties
System
ComponentModel
Composition
Hosting
System.Windows.Controls.Data.Input
Common
Properties
themes
Validation
Database
SchoolSample
SchoolSample.csproj.user
Asset
Control
Properties
View
SchoolSample.Common
Model
Properties
Resource
SchoolSample.Data
EntityModel
Properties
Validation
SchoolSample.Data.Wcf
EntityModel
SchoolModel.edmx
Properties
Validation
SchoolSample.Model
Properties
SchoolSample.ViewModel
Properties
SchoolSample.Wcf
SchoolSample.Wcf.csproj.user
Properties
Service
SchoolService.svc
SchoolSample.WCFService
SchoolSample.WCFService.csproj.user
Properties
Service References
SchoolService
configuration.svcinfo
configuration91.svcinfo
Reference.svcmap
SchoolService.disco
SchoolService.wsdl
Test.SchoolSample.Model
Properties
Test References
SchoolSample.Model.accessor
Test.SchoolSample.ViewModel
Properties
Test References
SchoolSample.ViewModel.accessor
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
SchoolSample.csproj.user
SchoolModel.edmx
SchoolSample.Wcf.csproj.user
SchoolService.svc
SchoolSample.WCFService.csproj.user
configuration.svcinfo
configuration91.svcinfo
Reference.svcmap
SchoolService.disco
SchoolService.wsdl
SchoolSample.Model.accessor
SchoolSample.ViewModel.accessor
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
SchoolSample.csproj.user
SchoolModel.edmx
SchoolSample.Wcf.csproj.user
SchoolService.svc
SchoolSample.WCFService.csproj.user
configuration.svcinfo
configuration91.svcinfo
Reference.svcmap
SchoolService.disco
SchoolService.wsdl
SchoolSample.Model.accessor
SchoolSample.ViewModel.accessor
//-----------------------------------------------------------------------
// <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.IsInDesignTool)
            //{
            //    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.RootVisual 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 = "Some error";//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)

Share

About the Author

Weidong Shen
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

You may also be interested in...

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160621.1 | Last Updated 20 Feb 2012
Article Copyright 2011 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid