I know this is going to be code overload...but here you go. I have tired to only include the parts I thought were relevant.
<code>
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MSPress.WebControls
{
/// <summary>
/// Summary description for TwoLevelDataGrid.
/// </summary>
[
ParseChildren(true),
DefaultEvent("SelectedIndexChanged"),
DefaultProperty("DataSource"),
Designer(typeof(MSPress.WebControls.Design.TreeDataGridDesigner), typeof(IDesigner))
]
public class TreeDataGrid : WebControl, INamingContainer, IPostBackEventHandler
{
private static readonly object EventSelectedIndexChanged = new object();
private static readonly object EventItemCreated = new object();
private static readonly object EventItemDataBound = new object();
private static readonly object EventItemCommand = new object();
public const string SelectCommandName = "Select";
public const string EditCommandName = "Edit";
public const string UpdateCommandName = "Update";
public const string CancelEditCommandName = "Cancel";
public const string DeleteCommandName = "Delete";
private bool renderClickSelectScript;
private DataKeyCollection dataKeys;
private TreeDataGridItem itemTemplate;
private TreeDataGridItem editItemTemplate;
private TreeDataGridItem headerTemplate;
private TreeDataGridItem footerTemplate;
private object dataSource;
private TableItemStyle itemStyle;
private TableItemStyle editItemStyle;
private TableItemStyle selectedItemStyle;
private TableItemStyle headerStyle;
private TableItemStyle footerStyle;
private TreeDataGridItemCollection items;
private TreeDataGridPanelStyle viewStyle;
#region Properties
[
Browsable(false),
DefaultValue(null),
Description("The template associated with all items"),
PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(TreeDataGridItem))
]
public TreeDataGridItem ItemTemplate
{
get
{
return itemTemplate;
}
set
{
itemTemplate = value;
}
}
[
Category("Data"),
DefaultValue(""),
Description("The name of the field that contains a key for each item")
]
public virtual string DataKeyField
{
get
{
string s = (string)ViewState["DataKeyField"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["DataKeyField"] = value;
}
}
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public DataKeyCollection DataKeys
{
get
{
if (dataKeys == null)
{
dataKeys = new DataKeyCollection(this.DataKeysArray);
}
return dataKeys;
}
}
private ArrayList DataKeysArray
{
get
{
object o = ViewState["DataKeys"];
if (o == null)
{
o = new ArrayList();
ViewState["DataKeys"] = o;
}
return (ArrayList)o;
}
}
[
Category("Data"),
DefaultValue(""),
Description("The name of the table within the data source")
]
public virtual string DataMember
{
get
{
string s = (string)ViewState["DataMember"];
return (s == null) ? String.Empty : s;
}
set
{
ViewState["DataMember"] = value;
}
}
[
Bindable(true),
Category("Data"),
DefaultValue(null),
Description("The data source containing data to be rendered"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public virtual object DataSource
{
get
{
return dataSource;
}
set
{
if ((value == null) || (value is IListSource) || (value is IEnumerable))
{
dataSource = value;
}
else
{
throw new ArgumentException();
}
}
}
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public TreeDataGridItemCollection Items
{
get
{
EnsureChildControls();
return items;
}
}
[
Bindable(true),
Category("Appearance"),
DefaultValue(1),
Description("The number of columns in the rendering")
]
public virtual int Columns
{
get
{
object i = ViewState["Columns"];
return (i == null) ? 1 : (int)i;
}
set
{
if (value < 1)
{
throw new ArgumentOutOfRangeException("value");
}
ViewState["Columns"] = value;
}
}
[
Category("Behavior"),
Description("Raised when an item is created")
]
public event TreeDataGridItemEventHandler ItemCreated
{
add
{
Events.AddHandler(EventItemCreated, value);
}
remove
{
Events.RemoveHandler(EventItemCreated, value);
}
}
[
Category("Behavior"),
Description("Raised when an item has been data-bound")
]
public event TreeDataGridItemEventHandler ItemDataBound
{
add
{
Events.AddHandler(EventItemDataBound, value);
}
remove
{
Events.RemoveHandler(EventItemDataBound, value);
}
}
[
Category("Action"),
Description("Raised when a control within an item raises a Command event")
]
public event TreeDataGridCommandEventHandler ItemCommand
{
add
{
Events.AddHandler(EventItemCommand, value);
}
remove
{
Events.RemoveHandler(EventItemCommand, value);
}
}
#endregion
#region Events
public override void DataBind()
{
// Data-bound controls implement custom data-binding logic by overriding
// this method.
// NOTE: We still want the DataBinding event to fire, so any <%# %> expressions
// on this control get evaluated first
base.OnDataBinding(EventArgs.Empty);
// Now re-create the new control hierarchy using the assigned data source.
Controls.Clear();
// We also want to throw out any view state for children if it exists because
// we're creating a new hierarchy. And then start tracking changes made
// during the data-binding process.
ClearChildViewState();
TrackViewState();
CreateControlHierarchy(true);
// Mark the flag indicating child controls have been created, so that
// CreateChildControls does not get called.
ChildControlsCreated = true;
}
protected virtual void OnSelectedIndexChanged(EventArgs e)
{
EventHandler handler = (EventHandler)Events[EventSelectedIndexChanged];
if (handler != null)
{
handler(this, null);
}
}
protected virtual void OnItemCreated(TreeDataGridItemEventArgs e)
{
TreeDataGridItemEventHandler handler = (TreeDataGridItemEventHandler)Events[EventItemCreated];
if (handler != null)
{
handler(this, null);
}
}
protected virtual void OnItemDataBound(TreeDataGridItemEventArgs e)
{
TreeDataGridItemEventHandler handler = (TreeDataGridItemEventHandler)Events[EventItemDataBound];
if (handler != null)
{
handler(this, null);
}
}
#endregion
#region Methods
protected override void CreateChildControls()
{
// If this gets called, we are re-creating children (the items)
// from view state.
Controls.Clear();
// We can create the items if we have view state, so we check for
// the number of Items we created during a previous request via a
// call to the DataBind method.
if (ViewState["Items"] != null)
{
CreateControlHierarchy(false);
}
}
protected virtual void CreateControlHierarchy(bool useDataSource)
{
IEnumerable dataSource = null;
int itemCount = 0;
this.items = null;
ArrayList dataKeysArray = DataKeysArray;
string dataKeyField = null;
if (useDataSource)
{
dataSource = GetDataSource();
dataKeysArray.Clear();
dataKeyField = DataKeyField;
}
else
{
dataSource = new object[(int)ViewState["Items"]];
}
if (dataSource != null)
{
Table outerTable = new Table();
Controls.Add(outerTable);
TreeDataGridItem headerItem = null;
TreeDataGridItem footerItem = null;
if (this.headerTemplate != null)
{
TableRow headerRow = new TableRow();
outerTable.Rows.Add(headerRow);
headerItem = CreateTreeDataGridItem(headerRow, -1, TreeDataGridItemType.Header, null, useDataSource);
}
TableRow bodyRow = new TableRow();
outerTable.Rows.Add(bodyRow);
TableCell bodyCell = new TableCell();
bodyRow.Cells.Add(bodyCell);
TreeDataGridPanel viewPanel = new TreeDataGridPanel();
bodyCell.Controls.Add(viewPanel);
TreeDataGridTable innerTable = CreateTreeDataGridTable();
viewPanel.Controls.Add(innerTable);
TableRow itemsRow = new TableRow();
innerTable.Rows.Add(itemsRow);
int editIndex = EditIndex;
int selectedIndex = SelectedIndex;
int itemIndex = 0;
foreach (object dataItem in dataSource)
{
TreeDataGridItemType itemType = TreeDataGridItemType.Item;
if (itemIndex == editIndex)
{
itemType |= TreeDataGridItemType.EditItem;
}
if (itemIndex == selectedIndex)
{
itemType |= TreeDataGridItemType.SelectedItem;
}
CreateTreeDataGridItem(itemsRow, itemIndex, itemType, dataItem, useDataSource);
itemIndex++;
itemCount++;
if (useDataSource && (dataKeyField.Length != 0))
{
dataKeysArray.Add(DataBinder.GetPropertyValue(dataItem, dataKeyField));
}
}
if (this.footerTemplate != null)
{
TableRow footerRow = new TableRow();
outerTable.Rows.Add(footerRow);
CreateTreeDataGridItem(footerRow, -1, TreeDataGridItemType.Footer, null, useDataSource);
}
this.items = CreateTreeDataGridItemCollection(itemsRow.Cells, headerItem, footerItem);
}
if (useDataSource)
{
ViewState["Items"] = itemCount;
}
}
protected override Style CreateControlStyle()
{
// Because TreeDataGrid renders an HTML table, an instance of
// a TableStyle is used as the control style.
TableStyle style = new TableStyle(ViewState);
// This is also the right spot to initialize the style
style.CellSpacing = 0;
return style;
}
protected virtual TreeDataGridItem CreateTreeDataGridItem(int itemIndex, TreeDataGridItemType itemType)
{
TreeDataGridItem item = new TreeDataGridItem(itemIndex, itemType, null);
item.ChildColumns.Add (ItemTemplate);
return item;
//return new TreeDataGridItem(itemIndex, itemType, this.ItemTemplate.TreeHeader);
}
private TreeDataGridItem CreateTreeDataGridItem(TableRow rowContainer, int itemIndex, TreeDataGridItemType itemType, object dataItem, bool dataBind)
{
TreeDataGridItem item = CreateTreeDataGridItem(itemIndex, itemType);
TreeDataGridItemEventArgs e = new TreeDataGridItemEventArgs(item);
TreeDataGridItem template = GetTemplateForItem(item);
if (template != null)
{
template.InstantiateIn(item);
}
OnItemCreated(e);
rowContainer.Cells.Add(item);
if (dataBind)
{
item.DataItem = dataItem;
item.DataBind();
OnItemDataBound(e);
}
return item;
}
protected virtual TreeDataGridItemCollection CreateTreeDataGridItemCollection(TableCellCollection cells, TreeDataGridItem headerItem, TreeDataGridItem footerItem)
{
return new TreeDataGridItemCollection(cells, headerItem, footerItem);
}
protected virtual TreeDataGridTable CreateTreeDataGridTable()
{
return new TreeDataGridTable();
}
protected virtual TreeDataGridItem GetTemplateForItem(TreeDataGridItem item)
{
TreeDataGridItem template = null;
switch (item.ItemType)
{
case TreeDataGridItemType.Header:
template = this.headerTemplate;
break;
case TreeDataGridItemType.Footer:
template = this.footerTemplate;
break;
default:
template = this.itemTemplate;
if ((item.ItemType & TreeDataGridItemType.EditItem) != 0)
{
if (this.editItemTemplate != null)
{
template = this.editItemTemplate;
}
}
break;
}
return template;
}
protected override bool OnBubbleEvent(object sender, EventArgs e)
{
TreeDataGridCommandEventArgs lce = e as TreeDataGridCommandEventArgs;
if (lce != null)
{
OnItemCommand(lce);
if (lce.CommandType == TreeDataGridCommandType.Select)
{
int oldSelectedIndex = SelectedIndex;
if (oldSelectedIndex != lce.Item.ItemIndex)
{
SelectedIndex = lce.Item.ItemIndex;
OnSelectedIndexChanged(EventArgs.Empty);
}
}
return true;
}
return false;
}
protected virtual void OnItemCommand(TreeDataGridCommandEventArgs e)
{
TreeDataGridCommandEventHandler handler = (TreeDataGridCommandEventHandler)Events[EventItemCommand];
if (handler != null)
{
handler(this, null);
}
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
}
protected virtual void PrepareControlHierarchyForRendering()
{
}
protected override void Render(HtmlTextWriter writer)
{
// Applying styles to the control hierarchy and then render it out.
// NOTE: Styles are applied as late as the render time.
// a) User can change styles after calling DataBind.
// b) Changes made to items during style application do not
// contribute to the view state. This control manages the
// state for styles, so having items manage it as well would
// be redundant.
PrepareControlHierarchyForRendering();
// NOTE: We don't render out tags corresponding to TreeDataGrid itself.
// We need to render its contents only. Therefore instead of calling
// base.Render, we call RenderContents.
RenderContents(writer);
}
}
using System;
using System.Collections;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MSPress.WebControls
{
/// <summary>
/// Summary description for TreeDataGridItem.
/// </summary>
[
ParseChildren(true),
ToolboxItem(false)
]
public class TreeDataGridItem : TableCell, INamingContainer, ITemplate
{
private int itemIndex;
private object dataItem;
private TreeDataGridItemType itemType;
private TreeDataGridChildren childColumns = new TreeDataGridChildren();
public TreeDataGridItem()
{
}
public TreeDataGridItem(int itemIndex, TreeDataGridItemType itemType, TreeDataGridPanel t)
{
this.itemIndex = itemIndex;
this.itemType = itemType;
if (t != null)
{
treeHeader = t;
//throw new Exception("Populate the header");
}
}
#region Properties
public TreeDataGridChildren ChildColumns
{
get
{
return childColumns;
}
set
{
childColumns = value;
}
}
public WebControl ChildColumn
{
get
{
return null;
}
set
{
childColumns.Add(value);
}
}
public virtual object DataItem
{
get
{
return this.dataItem;
}
set
{
this.dataItem = value;
}
}
public int ItemIndex
{
get
{
return this.itemIndex;
}
}
public virtual TreeDataGridItemType ItemType
{
get
{
return this.itemType;
}
}
public TreeDataGridPanel TreeHeader
{
get
{
return treeHeader;
}
set
{
treeHeader = value;
}
}
#endregion
protected override bool OnBubbleEvent(object sender, EventArgs e)
{
CommandEventArgs ce = e as CommandEventArgs;
if (ce != null)
{
TreeDataGridCommandEventArgs lce = new TreeDataGridCommandEventArgs(this, sender, ce);
RaiseBubbleEvent(this, lce);
return true;
}
return false;
}
public virtual void ResetItemType(TreeDataGridItemType newItemType)
{
this.itemType = newItemType;
}
#region ITemplate Members
public void InstantiateIn(Control container)
{
// TODO: Add TreeDataGridItem.InstantiateIn implementation
//Literal l = new Literal();
//l.Text = "testing";
//container.Controls.Add(l);
//if (treeHeader != null)
//{
//container.Controls.Add(treeHeader);
//}
if (this.childColumns != null)
{
foreach(WebControl child in childColumns)
{
container.Controls.Add(child);
}
}
EnsureChildControls();
}
protected override void Render(HtmlTextWriter writer)
{
this.CreateChildControls();
base.Render (writer);
}
#endregion
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
// TODO: Add TreeDataGridItem.GetEnumerator implementation
return null;
}
#endregion
}
public class TreeDataGridChildren : CollectionBase
{
public virtual void Add(WebControl child)
{
this.List.Add(child);
}
public virtual WebControl this[int index]
{
get
{
return (WebControl)this.List[index];
}
}
}
}
</code>
This code is based from:
Developing Microsoft ASP.NET Server Controls and Components |
By Nikhil Kothari, Vandana Datje |
Here is what's going on. First, the DataBind method gets called. This method then calls CreateControlHierarchy.
CreateControlHierarchy does the actual looping of the items in the datasource and creates a TreeDataGridItem per item in the datasource.
After these methods run, the overriden Render method gets called. Which in turn calls the TreeDataGridItem.Render().
This is what is displayed:
<table cellspacing="4" cellpadding="0" border="0" style="border-width:0px;height:100%;width:100%;">
<tr><td></td></tr>
<tr><td></td></tr>
.........MORE IDENTICAL ELEMENTS HERE .........
<tr><td></td></tr>
<tr><td></td></tr>
<tr>
<td>
<span id="listView1__ctl20_Label2">Straight Talk About Computers</span>
<div>
BLAh
</div>
<input name="listView1:_ctl20:Textbox2" type="text" id="listView1__ctl20_Textbox2" />
</td>
</tr>
</table>
I can seem to figure out why it work only for the last item! :)
Please let me know if there is an easier way to do this. Like I said all I want to do is add any number of controls in the .ASPX page and have them displayed per every item in the data source. Of course, once this is done I will wire up any server side functionality for the mentioned controls.