Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

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_20111201.zip
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
Settings.settings
themes
Validation
Database
SchoolSample
SchoolSample.csproj.user
Asset
Control
Properties
Settings.settings
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
SchoolSample_20120117.zip
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
Settings.settings
SchoolSample.csproj.user
Settings.settings
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
SchoolSample_20120216.zip
Local.testsettings
SchoolSample.vsmdi
TraceAndTestImpact.testsettings
GalaSoft.MvvmLight.Extras.WPF4.dll
GalaSoft.MvvmLight.WPF4.dll
Moq.dll
System.Windows.Interactivity.dll
WPFToolkit.Extended.dll
Settings.settings
SchoolSample.csproj.user
Settings.settings
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.Globalization;
using System.Windows.Controls.Common;
using System.Windows.Data;
using resources = System.Windows.Controls.Data.Input.Resources;

namespace System.Windows.Controls
{
    /// <summary>
    /// Displays a caption, required field indicator, and validation error indicator for a control.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)]
    [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)]
    [TemplateVisualState(Name = VisualStates.StateNotRequired, GroupName = VisualStates.GroupRequired)]
    [TemplateVisualState(Name = VisualStates.StateRequired, GroupName = VisualStates.GroupRequired)]
    [TemplateVisualState(Name = VisualStates.StateValid, GroupName = VisualStates.GroupValidation)]
    [TemplateVisualState(Name = VisualStates.StateInvalid, GroupName = VisualStates.GroupValidation)]
    public class DisplayLabel : ContentControl
    {
        #region Member fields

        private bool _initialized;
        private bool _isRequiredOverridden;
        
        // Set to true the Content property is not directly set. The Content property can then
        // get its value from the potential ValidationMetadata.Caption.
        private bool _canContentUseMetaData;

        // Set to true when the Content property is not explicitely set by the developer
        private bool _isContentBeingSetInternally;

        private List<ValidationError> _errors;

        #endregion Member fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the DisplayLabel class.
        /// </summary>
        public DisplayLabel()
        {
            this.DefaultStyleKey = typeof(DisplayLabel);
            this._errors = new List<ValidationError>();

            // Set binding to self for DataContext change notifications
            this.SetBinding(DisplayLabel.DataContextProperty, new Binding());
            this.Loaded += new RoutedEventHandler(this.Label_Loaded);
            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(this.Label_IsEnabledChanged);

            // Metadata can be consumed as long as the Content is still null.
            this._canContentUseMetaData = this.Content == null;

            //if (DesignerProperties.IsInDesignTool)
            //{
            //    this.SetContentInternally(typeof(DisplayLabel).Name);
            //}
        }

        #endregion Constructors

        #region Dependency Properties

        #region DataContext

        /// <summary>
        /// Identifies the DataContext dependency property.
        /// </summary>
        private static new readonly DependencyProperty DataContextProperty =
            DependencyProperty.Register(
            "DataContext",
            typeof(object),
            typeof(DisplayLabel),
            new PropertyMetadata(OnDataContextPropertyChanged));

        private static void OnDataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DisplayLabel label = d as DisplayLabel;
            if (label != null)
            {
                if (e.OldValue == null || e.NewValue == null || e.OldValue.GetType() != e.NewValue.GetType())
                {
                    // Refresh the metadata, but only if the DataContext's type has changed (or if either is null)
                    label.LoadMetadata(false);
                }
            }
        }

        #endregion DataContext

        #region IsRequired

        /// <summary>
        /// Identifies the IsRequired dependency property.
        /// </summary>
        public static readonly DependencyProperty IsRequiredProperty = DependencyProperty.Register(
            "IsRequired",
            typeof(bool),
            typeof(DisplayLabel),
            new PropertyMetadata(OnIsRequiredPropertyChanged));

        /// <summary>
        ///   Gets or sets a value that indicates whether 
        ///   the <see cref="P:System.Windows.Controls.DisplayLabel.Target" /> field is required. 
        /// </summary>
        public bool IsRequired
        {
            get
            {
                return (bool)GetValue(DisplayLabel.IsRequiredProperty);
            }

            set
            {
                this._isRequiredOverridden = true;
                SetValue(DisplayLabel.IsRequiredProperty, value);
            }
        }

        /// <summary>
        /// Handle the IsRequired field property change event.
        /// </summary>
        /// <param name="depObj">The DisplayLabel that had its IsRequired value changed.</param>
        /// <param name="e">The DependencyPropertyChangedEventArgs for this event.</param>
        private static void OnIsRequiredPropertyChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            // Dependency property changed
            DisplayLabel fl = depObj as DisplayLabel;
            if (fl != null)
            {
                fl.UpdateRequiredState();
            }
        }

        #endregion IsRequired

        #region IsValid

        /// <summary>
        /// Identifies the IsValid dependency property
        /// </summary>
        public static readonly DependencyProperty IsValidProperty =
            DependencyProperty.Register(
            "IsValid",
            typeof(bool),
            typeof(DisplayLabel),
            new PropertyMetadata(true, OnIsValidPropertyChanged));

        /// <summary>
        ///   Gets a value that indicates whether 
        ///   the <see cref="P:System.Windows.Controls.DisplayLabel.Target" /> field data is valid. 
        /// </summary>
        public bool IsValid
        {
            get { return (bool)GetValue(IsValidProperty); }
            private set { this.SetValueNoCallback(IsValidProperty, value); }
        }

        private static void OnIsValidPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DisplayLabel label = d as DisplayLabel;
            if (label != null && !label.AreHandlersSuspended())
            {
                label.SetValueNoCallback(DisplayLabel.IsValidProperty, e.OldValue);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, resources.UnderlyingPropertyIsReadOnly, "IsValid"));
            }
        }

        #endregion IsValid

        #region PropertyPath

        /// <summary>
        /// Identifies the PropertyPath dependency property
        /// </summary>
        public static readonly DependencyProperty PropertyPathProperty = DependencyProperty.Register(
            "PropertyPath",
            typeof(string),
            typeof(DisplayLabel),
            new PropertyMetadata(OnPropertyPathPropertyChanged));

        /// <summary>
        ///   Gets or sets the path to the dependency property on the 
        ///   <see cref="P:System.Windows.FrameworkElement.DataContext" /> of the 
        ///   <see cref="P:System.Windows.Controls.DisplayLabel.Target" /> control that this 
        ///   <see cref="T:System.Windows.Controls.DisplayLabel" /> is associated with. 
        /// </summary>
        public string PropertyPath
        {
            get { return GetValue(DisplayLabel.PropertyPathProperty) as string; }
            set { SetValue(DisplayLabel.PropertyPathProperty, value); }
        }

        private static void OnPropertyPathPropertyChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            DisplayLabel label = depObj as DisplayLabel;
            if (label != null && label.Initialized)
            {
                label.LoadMetadata(false);
                // Changing the PropertyPath sometimes requires an update for the validation state, since it might be stale.
                label.ParseTargetValidState();
            }
        }

        #endregion PropertyPath

        #region Target

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

        /// <summary>
        ///   Gets or sets the control that this <see cref="T:System.Windows.Controls.DisplayLabel" /> is associated with. 
        /// </summary>
        public FrameworkElement Target
        {
            get { return GetValue(TargetProperty) as FrameworkElement; }
            set { SetValue(TargetProperty, value); }
        }

        private static void OnTargetPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DisplayLabel label = d as DisplayLabel;
            if (label != null)
            {
                label.LoadMetadata(false);
                label._errors.Clear();
                FrameworkElement oldElement = e.OldValue as FrameworkElement;
                FrameworkElement newElement = e.NewValue as FrameworkElement;
                EventHandler<ValidationErrorEventArgs> bindingHandler = new EventHandler<ValidationErrorEventArgs>(label.Target_BindingValidationError);
                if (oldElement != null)
                {
                    Validation.RemoveErrorHandler(oldElement, bindingHandler);
                    //oldElement.BindingValidationError -= bindingHandler;
                }
                if (newElement != null)
                {
                    Validation.AddErrorHandler(newElement, bindingHandler);
                    //newElement.BindingValidationError += bindingHandler;
                    ReadOnlyObservableCollection<ValidationError> newErrors = Validation.GetErrors(newElement);
                    if (newErrors.Count > 0)
                    {
                        // Only the first error is used by binding
                        label._errors.Add(newErrors[0]);
                    }
                }

                label.ParseTargetValidState();
            }
        }

        #endregion Target

        #endregion Dependency Properties

        #region Properties

        /// <summary>
        /// Internally get or set the ValidationMetadata.  
        /// </summary>
        internal ValidationMetadata ValidationMetadata
        {
            get;
            set;
        }

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

        #endregion Properties

        #region Methods

        /// <summary>
        /// When the template is applied, this loads all the template parts
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            // Set default states
            this.UpdateValidationState();
            this.UpdateRequiredState();
        }

        /// <summary>
        /// Reload the metadata from the source target or DataContext
        /// </summary>
        public virtual void Refresh()
        {
            this._isRequiredOverridden = false;
            this.LoadMetadata(true /*forceUpdate*/);
            this.ParseTargetValidState();
        }

        /// <summary>
        /// Called when the value of the System.Windows.Controls.DisplayLabel.Content property changes.
        /// </summary>
        /// <param name="oldContent">The old value of the System.Windows.Controls.DisplayLabel.Content property.</param>
        /// <param name="newContent">The new value of the System.Windows.Controls.DisplayLabel.Content property.</param>
        protected override void OnContentChanged(object oldContent, object newContent)
        {
            base.OnContentChanged(oldContent, newContent);
            //if (DesignerProperties.IsInDesignTool)
            //{
            //    if (newContent == null)
            //    {
            //        this.SetContentInternally(typeof(DisplayLabel).Name);
            //    }
            //}

            // The Content property can consume the metadata when the developer has not set 
            // it explicitely, or when it was set to null.
            this._canContentUseMetaData = this._isContentBeingSetInternally || newContent == null;
        }

        /// <summary>
        /// Used internally to set the Content property at design-time or based on metadata.
        /// </summary>
        /// <param name="value">new value for the Content property</param>
        private void SetContentInternally(object value)
        {
            try
            {
                this._isContentBeingSetInternally = true;
                this.Content = value;
            }
            finally
            {
                this._isContentBeingSetInternally = false;
            }
        }

        /// <summary>
        /// IsEnabled property change handler
        /// </summary>
        /// <param name="sender">The DisplayLabel that had its IsEnabled value changed.</param>
        /// <param name="e">The DependencyPropertyChangedEventArgs for this event.</param>
        private void Label_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            this.UpdateCommonState();
        }

        /// <summary>
        /// Perform initialization code
        /// </summary>
        /// <param name="sender">The DisplayLabel that has loaded.</param>
        /// <param name="e">The RoutedEventArgs for this event.</param>
        private void Label_Loaded(object sender, RoutedEventArgs e)
        {
            if (!this._initialized)
            {
                // Loading Metadata onload because the dependency property could have changed before load and before the target was initialized
                this.LoadMetadata(false);
                this._initialized = true;
                this.Loaded -= new RoutedEventHandler(this.Label_Loaded);
            }
        }

        /// <summary>
        /// Load meta data and update the UI. 
        /// </summary>
        /// <param name="forceUpdate">If true, metadata will not be loaded from cache.</param>
        private void LoadMetadata(bool forceUpdate)
        {
            ValidationMetadata vmd = null;
            object entity = null;
            BindingExpression bindingExpression = null;
            if (!String.IsNullOrEmpty(this.PropertyPath))
            {
                entity = this.DataContext;
                // Pull metadata directly from the DataContext.  This isn't cached so it will be pulled every time.
                vmd = ValidationHelper.ParseMetadata(this.PropertyPath, entity);
            }
            else if (this.Target != null)
            {
                // Pull the metadata from the target FrameworkElement.  
                vmd = ValidationHelper.ParseMetadata(this.Target, forceUpdate, out entity, out bindingExpression);
            }
            if (this.ValidationMetadata != vmd)
            {
                this.ValidationMetadata = vmd;
                // Update to the new VMD
                if (this.ValidationMetadata != null)
                {
                    string newContent = this.ValidationMetadata.Caption;
                    if (newContent != null && this._canContentUseMetaData)
                    {
                        this.SetContentInternally(newContent);
                    }
                }
                else if (this._canContentUseMetaData)
                {
                    // The Target property was reset. Since the Content property
                    // was using the metadata, it also needs to be reset.
                    this.SetContentInternally(null);
                }
                
                if (!this._isRequiredOverridden)
                {
                    bool isRequired = this.ValidationMetadata == null ? false : this.ValidationMetadata.IsRequired;
                    SetValue(DisplayLabel.IsRequiredProperty, isRequired);
                }
            }
        }

        /// <summary>
        /// Parse the target error state and update the IsValid property
        /// </summary>
        private void ParseTargetValidState()
        {
            this.IsValid = this._errors.Count == 0;
            this.UpdateValidationState();
        }

        /// <summary>
        /// Event handler for target control's BindingValidationError event.
        /// </summary>
        /// <param name="sender">The target that had a BindingValidationError event.</param>
        /// <param name="e">The ValidationErrorEventArgs for this event.</param>
        private void Target_BindingValidationError(object sender, ValidationErrorEventArgs e)
        {
            if (e.Action == ValidationErrorEventAction.Added)
            {
                if (!this._errors.Contains(e.Error))
                {
                    this._errors.Add(e.Error);
                }
            }
            else if (e.Action == ValidationErrorEventAction.Removed)
            {
                if (this._errors.Contains(e.Error))
                {
                    this._errors.Remove(e.Error);
                }
            }

            this.ParseTargetValidState();
        }

        #region UpdateState

        /// <summary>
        /// Update the Common VSM state
        /// </summary>
        private void UpdateCommonState()
        {
            VisualStateManager.GoToState(this, this.IsEnabled ? VisualStates.StateNormal : VisualStates.StateDisabled, true);
        }

        /// <summary>
        /// Update the required field VSM state based on the IsRequired property.  
        /// </summary>
        private void UpdateRequiredState()
        {
            VisualStateManager.GoToState(this, this.IsRequired ? VisualStates.StateRequired : VisualStates.StateNotRequired, true);
        }

        /// <summary>
        /// Update the validation VSM state
        /// </summary>
        private void UpdateValidationState()
        {
            VisualStateManager.GoToState(this, this.IsValid ? VisualStates.StateValid : VisualStates.StateInvalid, true);
        }

        #endregion UpdateState

        #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

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 20 Feb 2012
Article Copyright 2011 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid