Click here to Skip to main content
15,886,519 members
Articles / Desktop Programming / WPF

A framework for comprehensive validation of user input

Rate me:
Please Sign up or sign in to vote.
4.70/5 (8 votes)
19 Jun 2012CPOL28 min read 31.2K   523   18  
Validation of input made as easy as possible for Windows.Forms, WPF, console-applications or any other purposes
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Threading;
using Ganzer;

namespace ValidatorTests.ViewModels
{
	//############################################################################
	/// <summary>
	/// The ViewModelBase class defines a basic view model that supports notification
	/// of changed properties.
	/// </summary>
	/// 
	public abstract class ViewModelBase : INotifyPropertyChanged
	{
		#region ViewModelBase
		#region types

		//############################################################################
		/// <summary>
		/// The DefaultMessageProvider class provides access to message boxes if no
		/// other provider is set by <see cref="MessageProvider"/>.
		/// </summary>
		/// 
		public class DefaultMessageProvider : IViewModelMessageProvider
		{
			#region IViewModelMessageProvider Member

			/// <summary>
			/// Displays a modal message box.
			/// </summary>
			/// 
			/// <param name="text">The message text to show.</param>
			/// <param name="caption">The text of the message box' title bar. If this is
			///   <c>null</c> <see cref="ApplicationInfo.Title"/> is used.</param>
			/// <param name="button">The button to show.</param>
			/// <param name="icon">The icon to display.</param>
			/// <param name="defResult">The default result.</param>
			/// <param name="options">The opitons to set.</param>
			/// 
			/// <returns>The result of the message box.</returns>
			/// 
			/// <remarks>
			/// The window that owns the shown message box ist set to the application's
			/// active window.
			/// </remarks>
			/// 
			public MessageBoxResult ShowMessageBox(
				string text,
				string caption = null,
				MessageBoxButton button = MessageBoxButton.OK,
				MessageBoxImage icon = MessageBoxImage.None,
				MessageBoxResult defResult = MessageBoxResult.OK,
				MessageBoxOptions options = MessageBoxOptions.None )
			{
				return ShowMessageBox(ViewModelBase.ActiveWindow, text, caption ?? ApplicationInfo.Title, button, icon, defResult, options);
			}

			/// <summary>
			/// Displays a modal message box.
			/// </summary>
			/// 
			/// <param name="owner">The window that owns the message box.</param>
			/// <param name="text">The message text to show.</param>
			/// <param name="caption">The text of the message box' name bar.</param>
			/// <param name="button">The button to show.</param>
			/// <param name="icon">The icon to display.</param>
			/// <param name="defResult">The default result.</param>
			/// <param name="options">The opitons to set.</param>
			/// 
			/// <returns>The result of the message box.</returns>
			/// 
			private MessageBoxResult ShowMessageBox(
				Window owner,
				string text,
				string caption,
				MessageBoxButton button,
				MessageBoxImage icon,
				MessageBoxResult defResult,
				MessageBoxOptions options )
			{
				if( owner != null && !owner.CheckAccess() )
				{
					return (MessageBoxResult)owner.Dispatcher.Invoke(
						new Func<Window, string, string, MessageBoxButton, MessageBoxImage, MessageBoxResult, MessageBoxOptions, MessageBoxResult>(ShowMessageBox),
						owner, text, caption, button, icon, defResult, options);
				}
				else
				{
					if( CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft )
						options |= MessageBoxOptions.RtlReading;

					if( owner != null )
						return MessageBox.Show(owner, text, caption, button, icon, defResult, options);

					return MessageBox.Show(text, caption, button, icon, defResult, options);
				}
			}

			#endregion
		}

		#endregion
		#region properties

		private string _name;

		/// <summary>
		/// Gets or sets the name of the view model.
		/// </summary>
		/// 
		/// <returns>The name of the view model or <c>null</c> if no name is set.
		///   </returns>
		/// 
		/// <remarks>
		/// <para>This property is not especially proposed to provide a unique
		/// identifier for a view model. It can be used freely and its meaning
		/// is defined by the application.
		/// </para>
		/// <para>The <see cref="PropertyChanged"/> event is raised when this property has
		/// changed. Changing this property does also raise the <see cref="PropertyChanged"/>
		/// event for <see cref="Title"/>.</para>
		/// </remarks>
		/// 
		public virtual string Name
		{
			get
			{
				return _name;
			}
			set
			{
				if( value == _name )
					return;

				_name = value;

				OnPropertyChanged("Name");
				OnPropertyChanged("Title");
			}
		}

		/// <summary>
		/// Gets the title of the view model.
		/// </summary>
		/// 
		/// <returns>This implementation does always return <see cref="Name"/>.
		///   </returns>
		/// 
		/// <remarks>
		/// <para>Implementors should build the title within its view models.
		/// This property is implemented to provide views a display text that
		/// i.e. contains a file name or modification marks beneath the name
		/// of the view model.
		/// </para>
		/// <para>The <see cref="PropertyChanged">PropertyChanged</see> event
		/// is raised when this property has changed.</para>
		/// </remarks>
		/// 
		public virtual string Title
		{
			get
			{
				return _name;
			}
		}

		private static IViewModelMessageProvider _messageProvider;

		/// <summary>
		/// Gets or sets an object that provides access to message boxes.
		/// </summary>
		/// 
		/// <returns>The set provider or <c>null</c> if no provider is set.</returns>
		/// 
		/// <remarks>
		/// If no provider is set, messages are shown by using the default message
		/// box of the current OS. The caption of this message box is set to the
		/// name of the application's executable file without the extension. The
		/// owner window of the message box is set to the currently active window.
		/// </remarks>
		/// 
		public static IViewModelMessageProvider MessageProvider
		{
			get
			{
				return _messageProvider;
			}
			set
			{
				_messageProvider = value;
			}
		}

		private static IViewModelMessageProvider _defaultMessageProvider;

		/// <summary>
		/// Gets a message provider independent of whether a provieder is set.
		/// </summary>
		/// 
		/// <returns>The message provider that is set by <see cref="MessageProvider"/>
		///   of a default provider if <see cref="MessageProvider"/> is not set.</returns>
		///   
		protected static IViewModelMessageProvider MessageProviderResolved
		{
			get
			{
				if( _messageProvider != null )
					return _messageProvider;

				if( _defaultMessageProvider == null )
					_defaultMessageProvider = new DefaultMessageProvider();

				return _defaultMessageProvider;
			}
		}

		/// <summary>
		/// Gets the currently active window.
		/// </summary>
		/// 
		/// <returns>The currently active window or <c>null</c> if the main window
		///   is not created yet.</returns>
		///   
		/// <remarks>
		/// This property is valid only if it is called within the thread that has
		/// created the application object.
		/// </remarks>
		/// 
		internal static Window ActiveWindow
		{
			get
			{
				foreach( Window window in Application.Current.Windows )
					if( window.IsActive )
						return window;

				return Application.Current.MainWindow;
			}
		}

		#endregion
		#region initialization

		/// <summary>
		/// Creates an empty instance.
		/// </summary>
		/// 
		protected ViewModelBase()
		{
		}

		/// <summary>
		/// Initializes this object with the specified argument.
		/// </summary>
		/// 
		/// <param name="name">The name to set.</param>
		/// 
		protected ViewModelBase( string name )
		{
			_name = name;
		}

		#endregion
		#endregion

		#region INotifyPropertyChanged Member
		#region events

		/// <summary>
		/// Raised when an observable property has changed.
		/// </summary>
		/// 
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Raises the <see cref="PropertyChanged"/> event.
		/// </summary>
		/// 
		/// <param name="propertyName">The name of the proiperty that has been changed.
		///   </param>
		/// 
		/// <remarks>
		/// Inheritors can override <see cref="OnPropertyChanged(PropertyChangedEventArgs)"/>
		/// to handle the event.
		/// </remarks>
		/// 
		protected void OnPropertyChanged( string propertyName )
		{
			OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
		}

		/// <summary>
		/// Raises the <see cref="PropertyChanged"/> event.
		/// </summary>
		/// 
		/// <param name="e">The event arguments.</param>
		/// 
		/// <remarks>
		/// Inheritors that overrides this method must call the base method to raise
		/// the <see cref="PropertyChanged"/> event.
		/// </remarks>
		/// 
		protected virtual void OnPropertyChanged( PropertyChangedEventArgs e )
		{
			Debug.Assert(e != null);

			if( e == null )
				throw new ArgumentNullException("e");

			VerifyPropertyName(e.PropertyName);

			if( PropertyChanged != null )
				PropertyChanged(this, e);
		}

		#endregion
		#region debugging

		/// <summary>
		/// Detects whether the specified string contains a valid property name for
		/// the current object.
		/// </summary>
		/// 
		/// <param name="name">The name of the property to verify.</param>
		/// 
		/// <remarks>
		/// This can be used to verify a string that contains a properrty name. It is
		/// used only if the symbol "DEBUG" is defined and calls <see cref="Debug.Fail(string)"/>
		/// if a property name with the specified name does not exist.
		/// </remarks>
		/// 
		[Conditional("DEBUG")]
		public void VerifyPropertyName( string name )
		{
			if( TypeDescriptor.GetProperties(this)[name] == null )
				Debug.Fail("Invalid Property Name.");
		}

		#endregion
		#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
Germany Germany
I am a software developer since many years and have worked on several large projects especially in financial sectors and the logistics industry.

My favorite programming languages are C, C++ und newly C#.

I am the architect and chief developer of Tricentis TDM Studio (former Q-up) - a generator that primarily creates template based synthetic data for software testing.

Comments and Discussions