jump to navigation

Insert a Dynamic Row into a DataGrid August 1, 2007

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

DataGrids (v1.1) and GridViews (v2.0) are extremely powerful controls for displaying databound data such as a table from a DB.  They also offer pagination and sorting for free.  However, for all that power at the end of the day they merely render a standard HTML table.  If you’re used to programmatically building your own tables and having special headers/footers or certain rows which behave differently the DataGrid can suddenly feel quite restricting.

 

Usually the place to start when cusomtizing the grid on the fly is the ItemDataBound event.  Here we have access to the DataGridItem and DataRowView representing the specific row being bound.  This allows us to easily test for certain conditions and modify the row.  But this event really limits you to affecting just the row being bound.

 

 

Last week  we needed to insert a new row under certain conditions.  Again, the best place to test if a specific row met these conditions was the ItemDataBound event.  If the condition was met, we wanted to add a row below the current row being bound.  How do we do this?

 

While you have access to the current row’s cells and to the grid itself, there is no obvious way of adding a new row.  The grid’s Items are available but these are bound to our datasource and looks like a dead end.  But they aren’t.  There is a way of adding in a new DataGridItem which translates to adding a new row.  Here’s how you do it:

 

 

    if (bConditionMet)

    {

        //Create an empty DataGridItem

        DataGridItem itemDesc = new DataGridItem(0, 0, ListItemType.Item);

        //This row will show the description of the item being bound

        TableCell cellDesc = new TableCell();

        cellDesc.Text = sDescription;

        cellDesc.ColumnSpan = e.Item.Cells.Count;

        itemDesc.Cells.Add(cellDesc);

        //Add the new row to the table                   

        m_dgItems.Controls[0].Controls.Add(itemDesc);

    }

 

Here we create a new DataGridItem and add cells which can contain text and/or their own controls.  To insert the DataGridItem into the grid is the tricky part.  We grab the first control in the DataGrid which is actually the DataGridTable.  As we are binding row by row the DataGridTable.Controls are filling with DataGridItems.  So, the DataGridTable.Controls only contains the DataGridItems bound thus far.  Hence, when we add our new DataGridItem it is the next row.

 

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);

}