jump to navigation

Data binding custom controls in GridView TemplateField columns May 22, 2006

Posted by codinglifestyle in ASP.NET, C#.
Tags: , , , , , , ,
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);

}

 

Advertisements

DataGrid ITemplate Columns: Programatically adding columns and wiring events for controls January 10, 2006

Posted by codinglifestyle in ASP.NET, C#.
Tags: , ,
add a comment

One of the great features of the DataGrid is that ability to add ITemplate derived columns which can host any control you like.  For example a column displaying a checkbox or radiobutton can leverage DataGrid’s select feature. It is trivial enough to add a ITemplate column and wire up events in the web designer, but a different story to do so programatically. 

  public class RadioButtonColumn : ITemplate
  {
   private RadioButton m_rb;

   public void InstantiateIn(Control container)
   {
    m_rb = new RadioButton();
    m_rb.AutoPostBack = true;
    m_rb.CheckedChanged +=new EventHandler(m_rb_CheckedChanged);

    container.Controls.Add(m_rb);
   }

   private void m_rb_CheckedChanged(object sender, EventArgs e)
   {
    RadioButton rb = (RadioButton) sender;
    DataGridItem container = (DataGridItem) rb.NamingContainer;
    DataGrid dg = (DataGrid)container.Parent.Parent;
   }
  }

When dynamically adding a ITemplate column to the datagrid control, ITemplate.InstantiateIn will be called when your datagrid is databinding.  So make sure to set up the grid’s columns before you DataBind().  The RadioButtonColumn class will simply display a radio button for each row when added to DataGrid.Columns (DataGrid.AutoGenerateColumns should = false).  However you will notice the event is never fired.

During Page postback the DataGrid.DataBind method will not be called in time to wire up the event because the datagrid columns will be re-created from the ViewState. To make the ITemplate column event’s work place your column into the datagrid before the LoadViewState method is called otherwise ITemplate.InstantiateIn will not be called.  In the ASP.NET lifecycle, LoadViewState method is called before the Load event
and after the Init event, so placing your code into the Init event will wire the event in time to be called.