Click here to Skip to main content
15,892,797 members
Articles / Desktop Programming / WPF

Smart WPF Login Overlay (Windows 8 Style)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (23 votes)
3 Dec 2012CPOL12 min read 135.1K   18.1K   76  
Login Overlay for WPF applications with a styling similar to the Windows 8 Login Screen.
//	--------------------------------------------------------------------
//		Member of the WPFSmartLibrary
//		For more information see : http://wpfsmartlibrary.codeplex.com/
//		(by DotNetMastermind)
//
//		filename		: SmartIndicator.xaml.cs
//		namespace	: SoftArcs.WPFSmartLibrary.SmartUserControls
//		class(es)	: SmartIndicator
//							
//	--------------------------------------------------------------------
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace SoftArcs.WPFSmartLibrary.SmartUserControls
{
	#region Enumerations

	/// <summary>
	/// Indicates the state of the indicator element
	/// </summary>
	public enum IndicatorLevel
	{
		Low,
		Middle,
		High
	}

	#endregion

	public partial class SmartIndicator
	{
		#region Fields

		private double oneThirdOfActualWidth;

		private DoubleAnimation moveAnimation;
		private DoubleAnimation onAnimation;
		private DoubleAnimation offAnimation;

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the Style associated with the control's internal Path object.
		/// </summary>
		public Style IndicatorStyle
		{
			get { return this.Indicator.Style; }
			set { this.Indicator.Style = value; }
		}

		#endregion

		#region Dependency Properties

		#region DependencyProperty - AnimationDuration (of type "Double")

		/// <summary>
		/// Gets or sets the duration of the indicator state changed animation (in milliseconds). This is a dependency property. The default value is 350.
		/// </summary>
		public double AnimationDuration
		{
			get { return (double)GetValue( AnimationDurationProperty ); }
			set { SetValue( AnimationDurationProperty, value ); }
		}
		public static readonly DependencyProperty AnimationDurationProperty =
			DependencyProperty.Register( "AnimationDuration", typeof( double ), typeof( SmartIndicator ),
												 new UIPropertyMetadata( 350.0,
																				(dpo, ea) =>
																				{
																					// ReSharper disable ConvertToLambdaExpression
																					((SmartIndicator)dpo).OnAnimationDurationChanged(
																						(double)ea.OldValue, (double)ea.NewValue );
																					// ReSharper restore ConvertToLambdaExpression
																				} ) );

		private void OnAnimationDurationChanged(double oldValue, double newValue)
		{
			if (oldValue.Equals( newValue )) return;

			this.moveAnimation.Duration = new Duration( TimeSpan.FromMilliseconds( newValue ) );
			this.onAnimation.Duration = new Duration( TimeSpan.FromMilliseconds( newValue ) );
			this.offAnimation.Duration = new Duration( TimeSpan.FromMilliseconds( newValue ) );
		}

		#endregion

		#region DependencyProperty - IndicatorState (of type "IndicatorLevel")

		/// <summary>
		/// Gets or sets the current indicator state. This is a dependency property. The default value is IndicatorLevel.Middle.
		/// </summary>
		public IndicatorLevel IndicatorState
		{
			get { return (IndicatorLevel)GetValue( IndicatorStateProperty ); }
			set { SetValue( IndicatorStateProperty, value ); }
		}
		public static readonly DependencyProperty IndicatorStateProperty =
									  DependencyProperty.Register( "IndicatorState", typeof( IndicatorLevel ), typeof( SmartIndicator ),
											new UIPropertyMetadata( IndicatorLevel.Middle,
												(dpo, ea) =>
												{
													// ReSharper disable ConvertToLambdaExpression
													((SmartIndicator)dpo).OnIndicatorStateChanged( (IndicatorLevel)ea.OldValue, (IndicatorLevel)ea.NewValue );
													// ReSharper restore ConvertToLambdaExpression
												} ) );

		private void OnIndicatorStateChanged(IndicatorLevel oldValue, IndicatorLevel newValue)
		{
			if (oldValue.Equals( newValue )) return;

			this.changeIndicatorLevel( newValue );
		}

		#endregion

		#region DependencyProperty - ObservedValue (of type "Double")

		/// <summary>
		/// Gets or sets the value being observed. This is a dependency property. The default value is 5.
		/// The Range for each level can be set with the FirstRangePerimeter and the SecondRangePerimeter properties.
		/// (The default value is exactly the half of the Slider's Maximum default value).
		/// </summary>
		public double ObservedValue
		{
			get { return (double)GetValue( ObservedValueProperty ); }
			set { SetValue( ObservedValueProperty, value ); }
		}
		public static readonly DependencyProperty ObservedValueProperty =
									  DependencyProperty.Register( "ObservedValue", typeof( double ), typeof( SmartIndicator ),
											new UIPropertyMetadata( 5.0,
												(dpo, ea) =>
												{
													// ReSharper disable ConvertToLambdaExpression
													((SmartIndicator)dpo).OnObservedValueChanged( (double)ea.OldValue, (double)ea.NewValue );
													// ReSharper restore ConvertToLambdaExpression
												} ) );

		private void OnObservedValueChanged(double oldValue, double newValue)
		{
			if (oldValue.Equals( newValue )) return;

			this.validateObservedValue();
		}

		#endregion

		#region DependencyProperty - ObservedPassword (of type "String")

		/// <summary>
		/// Gets or sets the password being observed. This is a dependency property.
		/// </summary>
		public string ObservedPassword
		{
			get { return (string)GetValue( ObservedPasswordProperty ); }
			set { SetValue( ObservedPasswordProperty, value ); }
		}
		public static readonly DependencyProperty ObservedPasswordProperty =
									  DependencyProperty.Register( "ObservedPassword", typeof( string ), typeof( SmartIndicator ),
											new UIPropertyMetadata( String.Empty,
												(dpo, ea) =>
												{
													// ReSharper disable ConvertToLambdaExpression
													((SmartIndicator)dpo).OnObservedPasswordChanged( (string)ea.OldValue, (string)ea.NewValue );
													// ReSharper restore ConvertToLambdaExpression
												} ) );

		private void OnObservedPasswordChanged(string oldValue, string newValue)
		{
			if (oldValue == null && newValue == null || oldValue != null && oldValue.Equals( newValue )) return;

			this.validateObservedPassword();
		}

		#endregion

		#region DependencyProperty - FirstRangePerimeter (of type "Double")

		/// <summary>
		/// Gets or sets the upper bound of the first level. This is a dependency property. The default value is 3.33.
		/// (The default value is exactly the third of the Slider's Maximum default value).
		/// </summary>
		public double FirstRangePerimeter
		{
			get { return (double)GetValue( FirstRangePerimeterProperty ); }
			set { SetValue( FirstRangePerimeterProperty, value ); }
		}
		public static readonly DependencyProperty FirstRangePerimeterProperty =
									  DependencyProperty.Register( "FirstRangePerimeter", typeof( double ), typeof( SmartIndicator ),
											new PropertyMetadata( 3.33,
												(dpo, ea) =>
												{
													// ReSharper disable ConvertToLambdaExpression
													((SmartIndicator)dpo).OnFirstRangePerimeterChanged( (double)ea.OldValue, (double)ea.NewValue );
													// ReSharper restore ConvertToLambdaExpression
												} ) );

		private void OnFirstRangePerimeterChanged(double oldValue, double newValue)
		{
			if (oldValue.Equals( newValue )) return;

			this.validateObservedValue();
		}

		#endregion

		#region DependencyProperty - SecondRangePerimeter (of type "Double")

		/// <summary>
		/// Gets or sets the upper bound of the first level. This is a dependency property. The default value is 6.67.
		/// (The default value is exactly two thirds of the Slider's Maximum default value).
		/// </summary>
		public double SecondRangePerimeter
		{
			get { return (double)GetValue( SecondRangePerimeterProperty ); }
			set { SetValue( SecondRangePerimeterProperty, value ); }
		}
		public static readonly DependencyProperty SecondRangePerimeterProperty =
									  DependencyProperty.Register( "SecondRangePerimeter", typeof( double ), typeof( SmartIndicator ),
											new PropertyMetadata( 6.67,
												(dpo, ea) =>
												{
													// ReSharper disable ConvertToLambdaExpression
													((SmartIndicator)dpo).OnSecondRangePerimeterPropertyChanged( (double)ea.OldValue, (double)ea.NewValue );
													// ReSharper restore ConvertToLambdaExpression
												} ) );

		private void OnSecondRangePerimeterPropertyChanged(double oldValue, double newValue)
		{
			if (oldValue.Equals( newValue )) return;

			this.validateObservedValue();
		}

		#endregion

		#endregion

		#region Constructor

		/// <summary>
		/// Initializes a new instance of the SmartIndicator class. 
		/// </summary>
		public SmartIndicator()
		{
			InitializeComponent();

			// Initialize all animation objects with the default values
			this.moveAnimation = new DoubleAnimation() { Duration = new Duration( TimeSpan.FromMilliseconds( this.AnimationDuration ) ) };
			this.onAnimation = new DoubleAnimation() { To = 0.9, Duration = new Duration( TimeSpan.FromMilliseconds( this.AnimationDuration ) ) };
			this.offAnimation = new DoubleAnimation() { To = 0.3, Duration = new Duration( TimeSpan.FromMilliseconds( this.AnimationDuration ) ) };
		}

		#endregion

		#region Events related to the Control

		/// <summary>
		/// Change the indicator level if the ObservedValueProperty was set on the control.
		/// </summary>
		private void SmartIndicator_Loaded(object sender, RoutedEventArgs e)
		{
			var observedValueSource = DependencyPropertyHelper.GetValueSource( this, ObservedValueProperty );

			if (observedValueSource.BaseValueSource != BaseValueSource.Default)
			{
				this.validateObservedValue();
			}

			var observedPasswordSource = DependencyPropertyHelper.GetValueSource( this, ObservedPasswordProperty );

			if (observedPasswordSource.BaseValueSource != BaseValueSource.Default)
			{
				this.validateObservedPassword();
			}
		}

		/// <summary>
		/// Calculate the third of the ActualWidth every time the size of the control is changed.
		/// </summary>
		private void SmartIndicator_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			this.oneThirdOfActualWidth = this.ActualWidth / 3.0;
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Change the indicator state depending on the observed value.
		/// </summary>
		private void validateObservedValue()
		{
			if (this.ObservedValue <= this.FirstRangePerimeter)
			{
				this.changeIndicatorLevel( IndicatorLevel.Low );
			}
			else if (this.ObservedValue > this.FirstRangePerimeter && this.ObservedValue <= SecondRangePerimeter)
			{
				this.changeIndicatorLevel( IndicatorLevel.Middle );
			}
			else
			{
				this.changeIndicatorLevel( IndicatorLevel.High );
			}
		}

		/// <summary>
		/// Change the indicator state depending on the observed password.
		/// </summary>
		private void validateObservedPassword()
		{
			if (this.ObservedPassword == null || this.ObservedPassword.Length < 3)
			{
				this.changeIndicatorLevel( IndicatorLevel.Low );
			}
			else if (this.ObservedPassword.Length >= 3 && this.ObservedPassword.Length < 5)
			{
				this.changeIndicatorLevel( IndicatorLevel.Middle );
			}
			else
			{
				this.changeIndicatorLevel( IndicatorLevel.High );
			}
		}

		/// <summary>
		/// Change the indicator state to the given IndicatorLevel with a smooth animation.
		/// </summary>
		private void changeIndicatorLevel(IndicatorLevel newLevel)
		{
			switch (newLevel)
			{
				case IndicatorLevel.Low:
					this.LowIndicator.BeginAnimation( OpacityProperty, onAnimation );
					this.MiddleIndicator.BeginAnimation( OpacityProperty, offAnimation );
					this.HighIndicator.BeginAnimation( OpacityProperty, offAnimation );
					moveAnimation.To = -this.oneThirdOfActualWidth;
					break;
				case IndicatorLevel.Middle:
					this.LowIndicator.BeginAnimation( OpacityProperty, offAnimation );
					this.MiddleIndicator.BeginAnimation( OpacityProperty, onAnimation );
					this.HighIndicator.BeginAnimation( OpacityProperty, offAnimation );
					moveAnimation.To = 0;
					break;
				case IndicatorLevel.High:
					this.LowIndicator.BeginAnimation( OpacityProperty, offAnimation );
					this.MiddleIndicator.BeginAnimation( OpacityProperty, offAnimation );
					onAnimation.To = 1.0;
					this.HighIndicator.BeginAnimation( OpacityProperty, onAnimation );
					onAnimation.To = 0.9;
					moveAnimation.To = this.oneThirdOfActualWidth;
					break;
				default:
					throw new ArgumentOutOfRangeException( "newLevel" );
			}

			this.IndicatorTranslateTransform.BeginAnimation( TranslateTransform.XProperty, moveAnimation );
		}

		#endregion
	}
}

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
Architect SoftArcs
Germany Germany
Experience:
More than 20 years with software design, architecture and development. More than 9 years with the .NET Framework.

Preferences:
C#, .NET 2.0, .NET 3.5, .NET 4.0, .NET 4.5, WPF (3, 4 and 4.5), MVVM, XAML, Silverlight, Windows Phone, Windows 8 Apps, ASP.NET, WCF, T-SQL and especially GUI-Development and GUI-Design.

I would say I am a dotNet Developer with a keen affinity for developing and enriching user interfaces.

Comments and Discussions