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.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.IsInDesignTool)
            //{
            //    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)

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
Web01 | 2.8.141015.1 | Last Updated 20 Feb 2012
Article Copyright 2011 by Weidong Shen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid