Click here to Skip to main content
15,881,898 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.4K   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.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.GetIsInDesignMode(this))
            {
                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.GetIsInDesignMode(this))
            {
                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)


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