|
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.
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.