Click here to Skip to main content
15,885,216 members
Articles / Web Development / ASP.NET

Data Binding in WebControls

Rate me:
Please Sign up or sign in to vote.
4.90/5 (43 votes)
4 Oct 2002CPOL6 min read 292.1K   3K   166  
An article on how to actually support data binding in your WebControl such that you can manipulate them in the Properties window.
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.Design;
using System.Collections;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Data;

// resolve ambiguous reference
using AttributeCollection = System.ComponentModel.AttributeCollection;


namespace ManyMonkeys.Web.ControlLibrary.Design
{
	/// <summary>
	/// This is the Designer class for the SimpleDataBoundControl Web Control.
	/// </summary>
	/// <remarks>
	/// The following articles are provided by MS and provided a good starting point into how to do 
	/// databinding however they were flawed.
	/// <ul>
	/// <li>You do need to support IDataSourceProvider if you only want to bind the datasource.</li>
	/// <li>You only need to implement IDataSourceProvider.GetSelectedDataSource() if you wish to support DataBinding on the DataMembers. However you
	/// need to modify the given example so that it will also allow IListSource - in fact I don't think there should be a filter there at all but it 
	/// doesn't do any harm at the moment.</li>
	/// <li>You only need to implement IDataSourceProvider.GetResolvedSelectedDataSource() if you wish to support DataBinding on Fields. However the 
	/// example given by MS was just plain wrong. See the code for an implementation that works.</li>
	/// </ul>
	/// <a target='_blank' href='http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconimplementingdataboundcontroldesingerwebforms.asp'>Microsoft Example</a><br/>
	/// <a target='_blank' href='http://groups.google.com/groups?th=368add3b0a1c58bc'>Google Groups Thread</a>
	/// </remarks>
	public class SimpleDataBoundControlDesigner : ControlDesigner , IDataSourceProvider
	{
		/// <summary>
		/// An empty constuctor.
		/// </summary>
		public SimpleDataBoundControlDesigner()
		{
		}

		#region Proxies of the properties that are involved in DataBinding
		/// <summary>
		/// This is a proxy for the DataMember field that is required to attach the DataMemberConverter to.
		/// </summary>
		public string DataMember
		{
			get
			{
				return ((SimpleDataBoundControl)this.Component).DataMember;
			}
			set
			{
				((SimpleDataBoundControl)this.Component).DataMember = value;
			}
		}

		/// <summary>
		/// This is a proxy for the DataTextField field that is required to attach the DataFieldConverter to.
		/// </summary>
		public string DataTextField
		{
			get
			{
				return ((SimpleDataBoundControl)this.Component).DataTextField;
			}
			set
			{
				((SimpleDataBoundControl)this.Component).DataTextField = value;
			}
		}

		/// <summary>
		/// This is a proxy for the DataValueField field that is required to attach the DataFieldConverter to.
		/// </summary>
		public string DataValueField
		{
			get
			{

				return ((SimpleDataBoundControl)this.Component).DataValueField;
			}
			set
			{
				((SimpleDataBoundControl)this.Component).DataValueField = value;
			}
		}

		/// <summary>
		/// This is a proxy for the DataSource field that is required to attach the DataSourceConverter to.
		/// This is especially required as it allows us to represent the DataSource property as a string
		/// rather then as an object.
		/// </summary>
		public string DataSource 
		{
			get 
			{
				DataBinding binding = DataBindings["DataSource"];
				if (binding != null) 
					return binding.Expression;
				return string.Empty;
			}
			set 
			{				
				if ((value == null) || (value.Length == 0)) 
					base.DataBindings.Remove("DataSource");
				else 
				{
					DataBinding binding = DataBindings["DataSource"];
					if (binding == null) 
						binding = new DataBinding("DataSource", 
							typeof(IEnumerable), value);
					else 
						binding.Expression = value;
					DataBindings.Add(binding);
				}

				OnBindingsCollectionChanged("DataSource");
			}
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Set to True so that the control can be resized on the form.
		/// </summary>
		public override bool AllowResize
		{
			get
			{
				return true;
			}
		}

		
		/// <summary>
		/// Modifies the control such that it will display Databound/Unbound as appropriate.
		/// </summary>
		/// <returns>The HTML representing our object at runtime.</returns>
		public override string GetDesignTimeHtml() 
		{
			// this lets us render the original drawing html but with the above mods that only appear at design time
			StringBuilder strbuild = new StringBuilder();
			HtmlTextWriter writer = new HtmlTextWriter(new StringWriter(strbuild));

			writer.RenderBeginTag(HtmlTextWriterTag.Div);
			
			if (DataSource.Length>0)
				writer.Write("Databound");
			else
				writer.Write("Unbound");

			writer.RenderEndTag();
			
			return strbuild.ToString();
		}
		

		/// <summary>
		/// Used to modify the Attributes of the 'Data' related fields such that
		/// the correct TypeConverters are added to the Attributes. For some reason
		/// adding the attributes directly doesn't work.
		/// </summary>
		/// <param name="properties">The dictionary</param>
		protected override void PreFilterProperties(IDictionary properties) 
		{
			base.PreFilterProperties(properties);
			PropertyDescriptor prop = (PropertyDescriptor)properties["DataSource"];
			if(prop!=null)
			{
				AttributeCollection runtimeAttributes = prop.Attributes;
				// make a copy of the original attributes but make room for one extra attribute ie the TypeConverter attribute
				Attribute[] attrs = new Attribute[runtimeAttributes.Count + 1];
				runtimeAttributes.CopyTo(attrs, 0);
				attrs[runtimeAttributes.Count] = new TypeConverterAttribute(typeof(DataSourceConverter));
				prop = TypeDescriptor.CreateProperty(this.GetType(), "DataSource", typeof(string),attrs);
				properties["DataSource"] = prop;
			}			

			prop = (PropertyDescriptor)properties["DataMember"];
			if(prop!=null)
			{
				AttributeCollection runtimeAttributes = prop.Attributes;
				Attribute[] attrs = new Attribute[runtimeAttributes.Count + 1];
				// make a copy of the original attributes but make room for one extra attribute ie the TypeConverter attribute
				runtimeAttributes.CopyTo(attrs, 0);
				attrs[runtimeAttributes.Count] = new TypeConverterAttribute(typeof(DataMemberConverter));
				prop = TypeDescriptor.CreateProperty(this.GetType(), "DataMember", typeof(string),attrs);
				properties["DataMember"] = prop;
			}			

			prop = (PropertyDescriptor)properties["DataValueField"];
			if(prop!=null)
			{
				AttributeCollection runtimeAttributes = prop.Attributes;
				Attribute[] attrs = new Attribute[runtimeAttributes.Count + 1];
				// make a copy of the original attributes but make room for one extra attribute ie the TypeConverter attribute
				runtimeAttributes.CopyTo(attrs, 0);
				attrs[runtimeAttributes.Count] = new TypeConverterAttribute(typeof(DataFieldConverter));
				prop = TypeDescriptor.CreateProperty(this.GetType(), "DataValueField", typeof(string),attrs);
				properties["DataValueField"] = prop;
			}			
			
			prop = (PropertyDescriptor)properties["DataTextField"];
			if(prop!=null)
			{
				AttributeCollection runtimeAttributes = prop.Attributes;
				Attribute[] attrs = new Attribute[runtimeAttributes.Count + 1];
				// make a copy of the original attributes but make room for one extra attribute ie the TypeConverter attribute
				runtimeAttributes.CopyTo(attrs, 0);
				attrs[runtimeAttributes.Count] = new TypeConverterAttribute(typeof(DataFieldConverter));
				prop = TypeDescriptor.CreateProperty(this.GetType(), "DataTextField", typeof(string),attrs);
				properties["DataTextField"] = prop;
			}			
		}
		#endregion
				
		#region IDataSourceProvider methods
		/// <summary>
		/// Used by the DataFieldConverter to resolve the DataSource and DataMember combination
		/// so that it can populate a dropdown with a list of available fields.
		/// </summary>
		IEnumerable IDataSourceProvider.GetResolvedSelectedDataSource() 
		{
			DataBinding binding;
			binding = this.DataBindings["DataSource"];
			if (binding != null)
				return DesignTimeData.GetSelectedDataSource(this.Component, binding.Expression, this.DataMember);
			return null;
		}

		/// <summary>
		/// Used by the DataMemberConverter to resolve the DataSource which it can then use
		/// to populate a drop down box containing a list of available tables.
		/// </summary>
		/// <returns>The object that is our DataSource</returns>
		object IDataSourceProvider.GetSelectedDataSource() 
		{
			DataBinding binding;

			binding = this.DataBindings["DataSource"];
			if (binding != null)
				return DesignTimeData.GetSelectedDataSource(this.Component, binding.Expression);
			return null;
		}
		#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
Employed (other) Purplebricks
Australia Australia
All articles are supplied as-is, as a howto on a particular task that worked for me in the past. None of the articles are supposed to be out-of-the-box freeware controls and nor should they be treated as such. Caveat emptor.

Now living and working in Australia, trying to be involved in the local .NET and Agile communities when I can.

I spend a good chunk of my spare time building OpenCover and maintaining PartCover both of which are Code Coverage utilities for .NET.

Comments and Discussions