Click here to Skip to main content
15,881,709 members
Articles / Programming Languages / C#

Databinding the SqlTypes

Rate me:
Please Sign up or sign in to vote.
4.70/5 (21 votes)
1 May 20032 min read 223.1K   3.7K   75  
Databinding the SqlTypes using the PropertyDescriptor class and the ITypedList Interface
using System;
using System.Collections;
using System.ComponentModel;
using System.Globalization;

namespace ITypedListExample
{
	/// <summary>
	/// We use array list so we dont have to write all those
	/// boring methods ( Count, Add(), RemoveAt() ...)
	/// </summary>
	[TypedCollection(typeof(MyData))]
	public class MyCollection : ArrayList, IBindingList , ITypedList
	{
		public MyCollection()
		{
			// discover the type inside the collections - this is jsut for fun.
			// could have been passed on the constructor
			foreach ( Attribute a in this.GetType().GetCustomAttributes(typeof(TypedCollectionAttribute),false) )
			{
				if ( a is TypedCollectionAttribute )
				{
					this.finalType = ((TypedCollectionAttribute)a).CollectionType;
					break;
				}
			}
			if ( this.finalType == null )
				throw new Exception("Must supply the type as a TypedCollectionAttibute of the class");
		}

		/// <summary>
		/// The type inside the collection
		/// </summary>
		private Type finalType = null;

		#region IBindingList

		private ListChangedEventArgs resetEvent = new ListChangedEventArgs(ListChangedType.Reset, -1);
		private ListChangedEventHandler onListChanged;

		protected virtual void OnListChanged(ListChangedEventArgs ev) 
		{
			if (onListChanged != null) 
			{
				onListChanged(this, ev);
			}
		}

		event ListChangedEventHandler IBindingList.ListChanged 
		{
			add 
			{
				onListChanged += value;
			}
			remove 
			{
				onListChanged -= value;
			}
		}
		
		bool IBindingList.AllowEdit 
		{ 
			get { return true ; }
		}

		bool IBindingList.AllowNew 
		{ 
			get { return true ; }
		}

		bool IBindingList.AllowRemove 
		{ 
			get { return true ; }
		}

		bool IBindingList.SupportsChangeNotification 
		{ 
			get { return false ; }
		}
        

		bool IBindingList.SupportsSearching 
		{ 
			get { return true ; }
		}

		bool IBindingList.SupportsSorting 
		{ 
			get { return true ; }
		}

		object IBindingList.AddNew() 
		{
			return finalType.GetConstructor(new Type[]{}).Invoke(null);
		}

		private bool isSorted = false;

		bool IBindingList.IsSorted 
		{ 
			get { return isSorted; }
		}

		private ListSortDirection listSortDirection = ListSortDirection.Ascending;
		
		ListSortDirection IBindingList.SortDirection 
		{ 
			get { return listSortDirection; }
		}

		PropertyDescriptor sortProperty = null;

		PropertyDescriptor IBindingList.SortProperty 
		{ 
			get { return sortProperty; }
		}

		void IBindingList.AddIndex(PropertyDescriptor property) 
		{
			isSorted = true;
			sortProperty = property;
		}

		void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) 
		{
			isSorted = true;
			sortProperty = property;
			listSortDirection = direction;

			ArrayList a = new ArrayList();

			this.Sort( new ObjectPropertyComparer(property.Name));
			if (direction == ListSortDirection.Descending) this.Reverse();
		}

		int IBindingList.Find(PropertyDescriptor property, object key) 
		{
			foreach( object o in this)
			{
				if ( Match( finalType.GetProperty(property.Name).GetValue(o,null) , key) ) 
					return this.IndexOf(o);
			}
			return -1;
		}

		void IBindingList.RemoveIndex(PropertyDescriptor property) 
		{
			sortProperty = null;
		}

		void IBindingList.RemoveSort() 
		{
			isSorted = false;
			sortProperty = null;
		}

		#endregion

		#region ITypedList
		PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
		{
			ArrayList input = null ;
			ArrayList output = new ArrayList();

			if ( listAccessors != null && listAccessors.Length > 0)
			{
				// if an listAccessors is suppled, we return the 
				// properties for the LAST one - dont ask me why - 
				// I found it in the sourse code for 
				// DataView.ITypedList.GetItemProperties using a 
				// decompiler

				PropertyDescriptor childProperty = listAccessors[listAccessors.Length - 1];

				Type t = null;

				foreach ( Attribute a in childProperty.Attributes )
				{
					if ( a is TypedCollectionAttribute )
					{
						t = ((TypedCollectionAttribute)a).CollectionType;
						break;
					}
				}

				if ( t != null )
					input = new ArrayList(TypeDescriptor.GetProperties(t));
			}
			else
			{
				input = new ArrayList(TypeDescriptor.GetProperties(finalType));
			}

			return GetPropertyDescriptorCollection(input);
		}

		string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
		{
			string name = "";

			if ( listAccessors != null )
			{
				foreach ( PropertyDescriptor p in listAccessors )
				{
					name += p.PropertyType.Name + "_";
				}
				name = name.TrimEnd('_');
			}
			else
				name = this.GetType().Name;

			return name;
		}
		#endregion

		#region Helper functions

		protected PropertyDescriptorCollection GetPropertyDescriptorCollection( ArrayList properties )
		{
			if ( properties == null || properties.Count == 0 )
				return new PropertyDescriptorCollection(null);

			ArrayList output = new ArrayList();

			foreach ( PropertyDescriptor p in properties )
			{
				if ( p.Attributes.Matches(new Attribute[]{new BindableAttribute(false)}) ) continue;

				if ( p.PropertyType.Namespace == "System.Data.SqlTypes" )
				{
					// create the base type property descriptor
					output.Add(SqlPropertyDescriptor.GetProperty( p.Name, p.PropertyType ) );
				}
				else
				{
					output.Add(p);
				}
			}
			return new PropertyDescriptorCollection((PropertyDescriptor[])output.ToArray(typeof(PropertyDescriptor)));
		}

		protected bool Match( object data, object searchValue )
		{
			// handle nulls
			if ( data == null || searchValue == null )
			{
				return (bool)(data == searchValue);
			}

			// if its a string, our comparisons should be 
			// case insensitive.
			bool IsString = (bool)(data is string);
			

			// bit of validation b4 we start...
			if ( data.GetType() != searchValue.GetType() )
				throw new ArgumentException("Objects must be of the same type");

			if ( ! (data.GetType().IsValueType || data is string ) )
				throw new ArgumentException("Objects must be a value type");

			

			/*
			 * Less than zero a is less than b. 
			 * Zero a equals b. 
			 * Greater than zero a is greater than b. 
			 */

			if ( IsString )
			{
				string stringData = ((string)data).ToLower(CultureInfo.CurrentCulture);
				string stringMatch = ((string)searchValue).ToLower(CultureInfo.CurrentCulture);

				return (bool)(stringData == stringMatch);			
			}		
			else
			{
				return (bool)(Comparer.Default.Compare(data,searchValue) == 0 );			
			}
		}

		#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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect support.com
Australia Australia

Comments and Discussions