Click here to Skip to main content
15,885,435 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.ComponentModel;
using System.Globalization;
using System.Windows.Automation.Peers;
using System.Windows.Controls.Common;
using System.Windows.Data;
using resources = System.Windows.Controls.Data.Input.Resources;

namespace System.Windows.Controls
{
    /// <summary>
    /// Displays a description and tracks error state for a control.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    [TemplateVisualState(Name = VisualStates.StateNormal, GroupName = VisualStates.GroupCommon)]
    [TemplateVisualState(Name = VisualStates.StateDisabled, GroupName = VisualStates.GroupCommon)]
    [TemplateVisualState(Name = VisualStates.StateNoDescription, GroupName = VisualStates.GroupDescription)]
    [TemplateVisualState(Name = VisualStates.StateHasDescription, GroupName = VisualStates.GroupDescription)]
    [TemplateVisualState(Name = VisualStates.StateValidFocused, GroupName = VisualStates.GroupValidation)]
    [TemplateVisualState(Name = VisualStates.StateValidUnfocused, GroupName = VisualStates.GroupValidation)]
    [TemplateVisualState(Name = VisualStates.StateInvalidFocused, GroupName = VisualStates.GroupValidation)]
    [TemplateVisualState(Name = VisualStates.StateInvalidUnfocused, GroupName = VisualStates.GroupValidation)]
    [StyleTypedProperty(Property = "ToolTipStyle", StyleTargetType = typeof(ToolTip))]
    public class DescriptionViewer : Control
    {
        #region Member fields

        private bool _descriptionOverridden;
        private bool _initialized;

        #endregion Member fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the DescriptionViewer class.
        /// </summary>
        public DescriptionViewer()
        {
            this.DefaultStyleKey = typeof(DescriptionViewer);
            // Set binding to self for DataContext change notifications
            this.SetBinding(DescriptionViewer.DataContextProperty, new Binding());
            this.Loaded += new RoutedEventHandler(this.DescriptionViewer_Loaded);
            this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(this.DescriptionViewer_IsEnabledChanged);
            if (DesignerProperties.GetIsInDesignMode(this))
            {
                this.Description = typeof(DescriptionViewer).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(DescriptionViewer),
            new PropertyMetadata(OnDataContextPropertyChanged));

        private static void OnDataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DescriptionViewer dv = d as DescriptionViewer;
            if (dv != 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)
                    dv.LoadMetadata(false);
                }
            }
        }

        #endregion DataContext

        #region Description

        /// <summary>
        /// Identifies the Description dependency property.
        /// </summary>
        public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
            "Description",
            typeof(string),
            typeof(DescriptionViewer),
            new PropertyMetadata(OnDescriptionPropertyChanged));

        /// <summary>
        /// Gets or sets the description text displayed by the viewer.
        /// </summary>
        public string Description
        {
            get
            {
                return GetValue(DescriptionProperty) as string;
            }

            set
            {
                this._descriptionOverridden = true;
                SetValue(DescriptionProperty, value);
            }
        }

        /// <summary>
        /// Handle the Description field property change event.  This will update the the VSM state.
        /// </summary>
        /// <param name="depObj">The DescriptionViewer that changed its Description value.</param>
        /// <param name="e">The DependencyPropertyChangedEventArgs for this event.</param>
        private static void OnDescriptionPropertyChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            // Dependency property changed
            DescriptionViewer dv = depObj as DescriptionViewer;
            if (dv != null)
            {
                dv.UpdateDescriptionState();
            }
        }

        #endregion Description

        #region GlyphTemplate

        /// <summary>
        /// Identifies the GlyphTemplate dependency property.
        /// </summary>
        public static readonly DependencyProperty GlyphTemplateProperty =
            DependencyProperty.Register(
            "GlyphTemplate",
            typeof(ControlTemplate),
            typeof(DescriptionViewer),
            null);

        /// <summary>
        /// Gets or sets the template that is used to display the description viewer glyph.
        /// </summary>
        public ControlTemplate GlyphTemplate
        {
            get { return GetValue(GlyphTemplateProperty) as ControlTemplate; }
            set { SetValue(GlyphTemplateProperty, value); }
        }

        #endregion GlyphTemplate

        #region ToolTipStyle

        /// <summary>
        /// Identifies the ToolTipStyle dependency property.
        /// </summary>
        public static readonly DependencyProperty ToolTipStyleProperty =
            DependencyProperty.Register(
            "ToolTipStyle",
            typeof(Style),
            typeof(DescriptionViewer),
            null);

        /// <summary>
        /// Gets or sets the style used to display tooltips.
        /// </summary>
        public Style ToolTipStyle
        {
            get { return GetValue(ToolTipStyleProperty) as Style; }
            set { SetValue(ToolTipStyleProperty, value); }
        }

        #endregion ToolTipStyle

        #region IsFocused

        /// <summary>
        /// Identifies the IsFocused dependency property
        /// </summary>
        public new static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.Register(
            "IsFocused",
            typeof(bool),
            typeof(DescriptionViewer),
            new PropertyMetadata(false, OnIsFocusedPropertyChanged));

        /// <summary>
        ///   Gets a value that indicates whether the <see cref="DescriptionViewer.Target" /> 
        ///   of the <see cref="DescriptionViewer" /> has focus. 
        /// </summary>
        protected new bool IsFocused
        {
            get { return (bool)GetValue(IsFocusedProperty); }
            private set { this.SetValueNoCallback(IsFocusedProperty, value); }
        }

        private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Label label = d as Label;
            if (label != null && !label.AreHandlersSuspended())
            {
                label.SetValueNoCallback(DescriptionViewer.IsFocusedProperty, e.OldValue);
                throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, resources.UnderlyingPropertyIsReadOnly, "IsFocused"));
            }
        }

        #endregion IsFocused

        #region IsValid

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

        /// <summary>
        ///   Gets a value that indicates whether the <see cref="DescriptionViewer.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)
        {
            DescriptionViewer dv = d as DescriptionViewer;
            if (dv != null && !dv.AreHandlersSuspended())
            {
                dv.SetValueNoCallback(DescriptionViewer.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(DescriptionViewer),
            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.Label.Target" /> control that 
        ///   this <see cref="T:System.Windows.Controls.DescriptionViewer" /> is associated with. 
        /// </summary>
        public string PropertyPath
        {
            get { return GetValue(DescriptionViewer.PropertyPathProperty) as string; }
            set { SetValue(DescriptionViewer.PropertyPathProperty, value); }
        }

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

        #endregion PropertyPath

        #region Target

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

        /// <summary>
        ///   Gets or sets the control that this <see cref="T:System.Windows.Controls.DescriptionViewer" /> 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)
        {
            DescriptionViewer dv = d as DescriptionViewer;
            if (dv != null)
            {
                bool targetFocused = e.NewValue == System.Windows.Input.FocusManager.GetFocusedElement(d);
                if (dv.IsFocused != targetFocused)
                {
                    dv.IsFocused = targetFocused;
                }
                dv.LoadMetadata(false);

                FrameworkElement oldElement = e.OldValue as FrameworkElement;
                FrameworkElement newElement = e.NewValue as FrameworkElement;
                EventHandler<ValidationErrorEventArgs> bindingHandler = new EventHandler<ValidationErrorEventArgs>(dv.Target_BindingValidationError);
                RoutedEventHandler gotFocusHandler = new RoutedEventHandler(dv.Target_GotFocus);
                RoutedEventHandler lostFocusHandler = new RoutedEventHandler(dv.Target_LostFocus);
                if (oldElement != null)
                {
                    Validation.RemoveErrorHandler(oldElement, bindingHandler);

                    //oldElement.BindingValidationError -= bindingHandler;
                    oldElement.GotFocus -= gotFocusHandler;
                    oldElement.LostFocus -= lostFocusHandler;
                }
                if (newElement != null)
                {
                    Validation.AddErrorHandler(newElement, bindingHandler);
                    //newElement.BindingValidationError += bindingHandler;
                    newElement.GotFocus += gotFocusHandler;
                    newElement.LostFocus += lostFocusHandler;
                }
                dv.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.UpdateDescriptionState();
        }

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

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

        /// <summary>
        /// Perform initialization code
        /// </summary>
        /// <param name="sender">The DescriptionViewer that has loaded.</param>
        /// <param name="e">The RoutedEventArgs for this event.</param>
        private void DescriptionViewer_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.DescriptionViewer_Loaded);
            }
        }

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

        /// <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._descriptionOverridden)
                {
                    string description = null;
                    if (this.ValidationMetadata != null)
                    {
                        description = this.ValidationMetadata.Description;
                    }
                    SetValue(DescriptionProperty, description);
                }
            }
        }

        /// <summary>
        /// Parse the target error state and update the IsValid property
        /// </summary>
        private void ParseTargetValidState()
        {
            if (!String.IsNullOrEmpty(this.PropertyPath))
            {
                // If PropertyPath is set, the IsValid state is not used and defaults to true, even if the PropertyPath is itself invalid.
                this.IsValid = true;
            }
            else if (this.Target != null)
            {
                this.IsValid = !Validation.GetHasError(this.Target);
            }
            else
            {
                // If no target is specified, IsValid state defaults back to true.
                this.IsValid = true;
            }
            this.UpdateValidationState();
        }

        /// <summary>
        /// Event handler for target control's BindingValidationError event.
        /// </summary>
        /// <param name="sender">The sender of the BindingValidationError event.</param>
        /// <param name="e">The ValidationErrorEventArgs for this event.</param>
        private void Target_BindingValidationError(object sender, ValidationErrorEventArgs e)
        {
            this.ParseTargetValidState();
        }

        /// <summary>
        /// Event handler for the target control's GotFocus event.
        /// </summary>
        /// <param name="sender">The sender of the GotFocus event.</param>
        /// <param name="e">The RoutedEventArgs for this event.</param>
        private void Target_GotFocus(object sender, RoutedEventArgs e)
        {
            this.IsFocused = true;
            this.UpdateValidationState();
        }

        /// <summary>
        /// Event handler for the target control's LostFocus event.
        /// </summary>
        /// <param name="sender">The sender of the LostFocus event.</param>
        /// <param name="e">The RoutedEventArgs for this event.</param>
        private void Target_LostFocus(object sender, RoutedEventArgs e)
        {
            this.IsFocused = false;
            this.UpdateValidationState();
        }

        #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 description property.  
        /// </summary>
        private void UpdateDescriptionState()
        {
            VisualStateManager.GoToState(this, String.IsNullOrEmpty(this.Description) ? VisualStates.StateNoDescription : VisualStates.StateHasDescription, true);
        }

        /// <summary>
        /// When updating the validation state, check the focus state and update the VSM accordingly
        /// </summary>
        private void UpdateValidationState()
        {
            if (this.IsValid)
            {
                if (this.IsFocused && !String.IsNullOrEmpty(this.Description))
                {
                    VisualStateManager.GoToState(this, VisualStates.StateValidFocused, true);
                }
                else
                {
                    VisualStateManager.GoToState(this, VisualStates.StateValidUnfocused, true);
                }
            }
            else
            {
                VisualStateManager.GoToState(this, this.IsFocused ? VisualStates.StateInvalidFocused : VisualStates.StateInvalidUnfocused, 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