Click here to Skip to main content
15,894,646 members
Articles / Desktop Programming / WPF

WPF Control Factory

Rate me:
Please Sign up or sign in to vote.
4.25/5 (7 votes)
20 Apr 2010CPOL6 min read 38K   418   16  
This article explains some advantages and disadvantages of factories, and shows one to use for generating WPF Controls.
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using Pfz.DataTypes;
using Pfz.Ranges;

namespace Pfz.WpfControls
{
	/// <summary>
	/// Control used to edit ranges. It displays a slider and a text-box for editing
	/// values.
	/// </summary>
	/// <typeparam name="TRange">The type of the range used.</typeparam>
	public class RangeControl<TRange>:
		Grid,
		IValueControl,
		IValueContainer<TRange>
	where
		TRange: IRange
	{
		private Slider fSlider = new Slider();
		private TextBox fTextBox = new TextBox();
		internal ColumnDefinition fTextBoxColumn;
		
		private static readonly Type fPropertyDataType;
		private static readonly ConstructorInfo fRangeConstructor;
		
		/// <summary>
		/// Gets some information for the Range type only once.
		/// </summary>
		static RangeControl()
		{
			var propertyType = typeof(TRange);
			while(propertyType != null)
			{
				if (propertyType.IsGenericType)
				{
					if (propertyType.GetGenericTypeDefinition() == typeof(Range<>))
					{
						Type dataType = propertyType.GetGenericArguments()[0];
						fPropertyDataType = dataType;
						fRangeConstructor = typeof(TRange).GetConstructor(new Type[]{dataType});
						return;
					}
				}
				
				propertyType = propertyType.BaseType;
			}
			
			throw new ApplicationException("Can't find the Range constructor.");
		}
		
		/// <summary>
		/// Creates a new RangeControl.
		/// </summary>
		public RangeControl()
		{
			ColumnDefinition columnSlider = new ColumnDefinition();
			columnSlider.Width = new GridLength(1, GridUnitType.Star);
			ColumnDefinitions.Add(columnSlider);

			ColumnDefinition columnTextBox = new ColumnDefinition();
			columnTextBox.Width = new GridLength(100, GridUnitType.Pixel);
			ColumnDefinitions.Add(columnTextBox);
			fTextBoxColumn = columnTextBox;

			RowDefinition row = new RowDefinition();
			row.Height = new GridLength(1, GridUnitType.Star);
			RowDefinitions.Add(row);
			
			Children.Add(fSlider);
			Children.Add(fTextBox);

			Grid.SetColumn(fSlider, 0);
			Grid.SetRow(fSlider, 0);
			Grid.SetColumn(fTextBox, 1);
			Grid.SetRow(fTextBox, 0);

			fSlider.ValueChanged += fSlider_ValueChanged;
			fTextBox.LostFocus += fTextBox_LostFocus;

			var attribute = Range.GetRangeAttribute(typeof(TRange));
			fSlider.Minimum = Convert.ToDouble(attribute.MinimumValue);
			fSlider.Maximum = Convert.ToDouble(attribute.MaximumValue);
			p_GetSliderValue();
		}
		
		void fTextBox_LostFocus(object sender, RoutedEventArgs e)
		{
			try
			{
				double result;
				if (double.TryParse(fTextBox.Text, out result))
					fSlider.Value = result;
				else
					p_GetSliderValue();
			}
			catch
			{
				p_GetSliderValue();
			}
		}

		void fSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
		{
			p_GetSliderValue();
		}
		private void p_GetSliderValue()
		{
			var dataType = fPropertyDataType;
			if (dataType == null)
			{
				fTextBox.Text = fSlider.Value.ToString();
				return;
			}
			
			object value = Convert.ChangeType(fSlider.Value, dataType);
			fTextBox.Text = value.ToString();
		}
		
		/// <summary>
		/// Clears the value of this control.
		/// </summary>
		public void Clear()
		{
			fSlider.Value = fSlider.Minimum;
		}
		
		/// <summary>
		/// Gets or sets a value indicating that this control should be read-only.
		/// </summary>
		public bool IsReadOnly
		{
			get
			{
				return !IsEnabled;
			}
			set
			{
				IsEnabled = !value;
			}
		}
		
		/// <summary>
		/// Gets or sets the range value.
		/// </summary>
		public TRange Value
		{
			get
			{
				var value = fSlider.Value;
				var typedValue = Convert.ChangeType(value, fPropertyDataType);
				var range = fRangeConstructor.Invoke(new object[]{typedValue});
				return (TRange)range;
			}
			set
			{
				if (value == null)
					fSlider.Value = 0;
				else
					fSlider.Value = Convert.ToDouble(value.Value);
			}
		}
		
		#region IValueContainer Members
			object IValueContainer.Value
			{
				get
				{
					return Value;
				}
				set
				{
					Value = (TRange)value;
				}
			}
		#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
Software Developer (Senior) Microsoft
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at: http://paulozemek.azurewebsites.net/
Or e-mail me at: paulozemek@outlook.com

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (in October 2014 I started working at Microsoft, so I can't be a Microsoft MVP anymore).

Comments and Discussions