///
/// A templated databound repeater that dynamically determines which
/// template to use based on the class of the object in the collection.
///
[ParseChildren(false)]
[ControlBuilder(typeof(ObjectRepeaterControlBuilder))]
public class ObjectRepeater : System.Web.UI.Control, System.Web.UI.INamingContainer, IParserAccessor
{
private Hashtable _templates = new Hashtable();
private string _defaultTemplateName = null;
private IEnumerable _dataSource;
public ObjectRepeaterDetermineTemplateDelegate DetermineTemplate;
public event RepeaterItemEventHandler ItemCreated;
public event RepeaterItemEventHandler ItemDataBound;
public event RepeaterCommandEventHandler ItemCommand;
#region Properties
public string DefaultTemplate
{
get
{
return _defaultTemplateName;
}
set
{
_defaultTemplateName = value;
}
}
public virtual IEnumerable DataSource
{
get
{
return _dataSource;
}
set
{
_dataSource = value;
}
}
public ControlCollection Items
{
get
{
return this.Controls;
}
}
#endregion
#region Methods
public void OnItemDataBound(RepeaterItemEventArgs e)
{
if(this.ItemDataBound != null)
this.ItemDataBound(this, e);
}
public void OnItemCreated(RepeaterItemEventArgs e)
{
if(this.ItemCreated != null)
this.ItemCreated(this, e);
}
public void OnItemCommand(RepeaterCommandEventArgs e)
{
if(this.ItemCommand != null)
this.ItemCommand(this, e);
}
public override void DataBind()
{
// Controls with a data-source property perform their
// custom data binding by overriding DataBind to
// evaluate any data-binding expressions on the control
// itself.
base.OnDataBinding(EventArgs.Empty);
// Reset the control's state.
Controls.Clear();
ClearChildViewState();
// Create the control hierarchy using the data source.
CreateControlHierarchy(true);
ChildControlsCreated = true;
TrackViewState();
}
protected override void CreateChildControls()
{
Controls.Clear();
if (ViewState["ItemCount"] != null)
{
// Create the control hierarchy using the view state,
// not the data source.
CreateControlHierarchy(false);
}
}
///
///
///
/// True to create the hierarchy from the DataSource, False to create it from the ViewState.
private void CreateControlHierarchy(bool useDataSource)
{
IEnumerable dataSource = null;
int count = -1;
if (useDataSource == false)
{
// ViewState must have a non-null value for ItemCount because this is checked
// by CreateChildControls.
count = (int)ViewState["ItemCount"];
if (count != -1)
{
dataSource = new DummyDataSource(count);
}
}
else
{
dataSource = this._dataSource;
}
if (dataSource != null)
{
int index = 0;
count = 0;
foreach (object dataItem in dataSource)
{
CreateItem(index, ListItemType.Item, useDataSource, dataItem);
count++;
index++;
}
}
if (useDataSource)
{
// Save the number of items contained for use in round trips.
ViewState["ItemCount"] = ((dataSource != null) ? count : -1);
}
}
private RepeaterItem CreateItem(int itemIndex, ListItemType itemType, bool dataBind, object dataItem)
{
RepeaterItem item = new RepeaterItem(itemIndex, itemType);
RepeaterItemEventArgs e = new RepeaterItemEventArgs(item);
//decide which template to use.
string templateName = null;
if(dataBind)
{
if(DetermineTemplate != null)
{
templateName = this.DetermineTemplate(this, dataItem);
ViewState["templateName" + itemIndex.ToString()] = templateName;
}
}
else
{
//determine template to use from viewState;
templateName = (string)ViewState["templateName" + itemIndex.ToString()];
}
if(templateName == null)
templateName = this.DefaultTemplate;
ObjectTemplate dynamicTemplate = (ObjectTemplate)_templates[templateName];
//Must exist.
dynamicTemplate.ItemTemplate.InstantiateIn(item);
if (dataBind)
{
item.DataItem = dataItem;
}
OnItemCreated(e);
this.Controls.Add(item);
if (dataBind)
{
item.DataBind();
OnItemDataBound(e);
item.DataItem = null;
}
return item;
}
protected override bool OnBubbleEvent(object source, EventArgs e)
{
// Handle events raised by children by overriding OnBubbleEvent.
bool handled = false;
if (e is CommandEventArgs)
{
RepeaterCommandEventArgs ce = (RepeaterCommandEventArgs)e;
OnItemCommand(ce);
handled = true;
}
return handled;
}
///
/// This member overrides Control.AddParsedSubObject.
/// it catches the contents of each item.
///
protected override void AddParsedSubObject(object obj)
{
if(obj is ObjectTemplate)
{
ObjectTemplate template = (ObjectTemplate)obj;
_templates.Add(template.Name, template);
return;
}
else
{
if(!(obj is LiteralControl))
throw new Exception("ObjectRepeater can only have children of type 'ObjectTemplate'");
}
base.AddParsedSubObject(obj);
}
#endregion
}
public delegate string ObjectRepeaterDetermineTemplateDelegate(object sender, object dataItem);
#region Control Builder
///
/// Interacts with the parser to build a PanelBar control.
///
public class ObjectRepeaterControlBuilder : ControlBuilder
{
///
/// This member overrides ControlBuilder.GetChildControlType.
///
public override Type GetChildControlType(string tagName, IDictionary attributes)
{
// check is the tag is an TabStripPanelItem tag
if (tagName.ToLower().IndexOf("objecttemplate") >= 0)
{
System.Diagnostics.Trace.WriteLine(tagName);
// yes, so return TabStripPanelItem type
return typeof(ObjectTemplate);
}
return null;
}
public override void AppendLiteralString(string s)
{
//empty implementation to drop literal content.
System.Diagnostics.Trace.WriteLine(s);
}
}
#endregion
[ParseChildren(true)]
public class ObjectTemplate : Control
{
private string _name;
private ITemplate _itemTemplate;
[TemplateContainer(typeof(RepeaterItem))]
public ITemplate ItemTemplate
{
get { return _itemTemplate; }
set { _itemTemplate = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
}
internal sealed class DummyDataSource : ICollection
{
private int dataItemCount;
public DummyDataSource(int dataItemCount)
{
this.dataItemCount = dataItemCount;
}
public int Count
{
get
{
return dataItemCount;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public bool IsSynchronized
{
get
{
return false;
}
}
public object SyncRoot
{
get
{
return this;
}
}
public void CopyTo(Array array, int index)
{
for (IEnumerator e = this.GetEnumerator(); e.MoveNext();)
array.SetValue(e.Current, index++);
}
public IEnumerator GetEnumerator()
{
return new BasicDataSourceEnumerator(dataItemCount);
}
private class BasicDataSourceEnumerator : IEnumerator
{
private int count;
private int index;
public BasicDataSourceEnumerator(int count)
{
this.count = count;
this.index = -1;
}
public object Current
{
get
{
return null;
}
}
public bool MoveNext()
{
index++;
return index < count;
}
public void Reset()
{
this.index = -1;
}
}
}