Data binding custom controls in GridView TemplateField columns May 22, 2006
Posted by codinglifestyle in ASP.NET, C#.Tags: composite control, databind, datasource, dynamic control, edit, gridview, ITemplateField, TemplateField
add a comment
Last week I began implementing an ASP.NET 2.0 application which required a custom control to be displayed in a GridView. I’ve posted a couple times on the ITemplate class in relation to the DataGrid however this time several details in databinding took some time to sort out.
This application utilizes a SQLDataSource with SelectCommand, InsertCommand, UpdateCommand, and DeleteCommand defined. The GridView has DataSourceID=”MyDataSource” DataKeyNames=”ID” defined. In addition AutoGenerateColumns is false, each column is a TemplateField with a ItemTemplate as a label but a EditTemplate to be my custom control. That is, only when editing is my custom control displayed. The custom control and EditTemplate are to be set programmatically which leaves the .aspx for the <Columns> looking like:
<asp:TemplateField HeaderText=”Statement” SortExpression=”Statement”>
<ItemTemplate>
<asp:Label ID=”Statement1″ runat=”server” Text=’<%# Bind(“Statement”) %>‘></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:CommandField ShowEditButton=”True” />
<asp:TemplateField>
<ItemTemplate>
<asp:ImageButton ID=”DeleteButton” Runat=”server” ImageUrl=”~/images/delete.gif” OnClientClick=”return confirm(‘Are you sure you want to delete this rule?’);” ToolTip=”Delete” CommandName=”Delete” />
</ItemTemplate>
</asp:TemplateField>
Note the command field which will allow in-place row editing and a straight-forward TemplateField which implements the delete command but with a javascript confirmation. These hook to the Update and Delete commands in the data source.
The EditTemplate will contain my custom control databound to the datasource. Rather than ITemplate we use IBindableTemplate which gives us the ExtractValues method. The class, which is implementing a combobox, look like this:
public abstract class ComboCol : System.Web.UI.Page, IBindableTemplate
{
protected string m_sCol;
protected ComboBox m_cCombo;
protected GridViewRow m_gvRow;
protected DataRowView m_dRow;
public ComboCol(string sCol)
{
m_sCol = sCol;
}
public virtual void OnInit(object sender, EventArgs e)
{ }
public void InstantiateIn(System.Web.UI.Control container)
{
m_cCombo = new ComboBox();
m_cCombo.ID = m_sCol;
m_cCombo.Init +=new EventHandler(OnInit);
m_cCombo.DataBinding += new EventHandler(cCombo_DataBinding);
container.Controls.Add(m_cCombo);
}
public void cCombo_DataBinding(object sender, EventArgs e)
{
m_cCombo = (ComboBox)sender;
m_gvRow = (GridViewRow)m_cCombo.NamingContainer;
m_dRow = (DataRowView)m_gvRow.DataItem;
//Just to show how to get the value
object dataValue = DataBinder.Eval(m_gvRow.DataItem, m_sCol);
OnDataBind();
}
//Force derived classes to implement
public abstract void OnDataBind();
public IOrderedDictionary ExtractValues(Control container)
{
OrderedDictionary dict = new OrderedDictionary();
string value = m_cCombo.SelectedValue.ToString();
dict.Add(m_sCol, value);
return dict;
}
}
Note this is an abstract base class, so the derived class must implement OnDataBind which might look like this:
public override void OnDataBind()
{
m_cCombo.SelectedValue = m_dRow[m_sCol].ToString().Trim().ToUpper();
}
Now for the trick which took me some time to figure out. While breaking on ExtractValues() my control was not always created. It’s a bit tough to extract the value I want to send to the datasource when the order of execution hasn’t created my control yet. This was due to where I was assigning my EditTemplate which created the column and thus the control. Originally I has placed this in CreateChildControls() in the code-behind of the page. Placing the code in the DataGrid’s Init event did the trick. So creating the edit template looks something like this:
protected void CRuleGrid_Init(object sender, EventArgs e)
{
((TemplateField)CRuleGrid.Columns[STATEMENT_COL]).EditItemTemplate = new OpCol(“Statement”, OPS2);
}