Question

Situation: I have several Gridviews on one page, and each Gridview has a dynamically created row to display the totals at the bottom. In each case, the totals row is created on a RowDataBound event. The strategy that I am using is like the one provided by Mike Dugan on his Blog.

The following is the code for one of the GridViews, but the others all do something very simular.

protected void gvWorkerHours_RowDataBound(object sender, GridViewRowEventArgs e)
{
    // Keep running total of hours.  
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        totalBillableHours += Convert.ToDouble(DataBinder.Eval(e.Row.DataItem, "Hours"));
    }

    if (e.Row.RowType == DataControlRowType.Footer)
    {
        int numColumns = gvWorkerHours.Columns.Count;
        int hoursIndex = 4; //5th column, 0-based
        int rowIndex = gvWorkerHours.Rows.Count + 1;

        CreateTotalRow((Table)e.Row.Parent, rowIndex, totalBillableHours, hoursIndex, numColumns);
    }
}

private void CreateTotalRow(Table table, int rowIndex, double totalValue, int totalIndex, int numColumns)
{
    TableCell[] cells = new TableCell[numColumns];
    for (int i = 0; i < numColumns; i++)
    {
        TableCell cell = new TableCell();
        Label label = new Label();
        label.Font.Bold = true;

        if (i == 0)
        {
            label.Text = "Total";
        }
        else if (i == totalIndex)
        {
            label.Text = totalValue.ToString();
        }
        else
        {
            label.Text = "";
        }

        cell.Controls.Add(label);
        cells[i] = cell;
    }
    GridViewRow row = new GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal);
    row.Cells.AddRange(cells);
    table.Rows.AddAt(rowIndex, row);
}

Problem: If a user clicks on an edit/delete command for any row on any of these Gridviews, it will make the totals row disappear for all other Gridviews. As I understand, this is because a PostBack is occurring, however the RowDataBound events will not occur for the other GridViews, rather they will just reload their data from the ViewState, which does not contain the totals.

Failed attempts at solving: I cannot simply call DataBind on each of the GridView during a PostBack, because that will prevent the update/delete from occurring. Although the RowCreated event will occur for the GridViews during a PostBack, this event in not sufficient because the GridViews will not have data bound and will throw an exception when I try to calculate the total. Disabling the ViewState for these GridViews seems like a solution, however there will be a lot of data to reload each time a user clicks a command. Manually saving my data to the ViewState also seems like a solution, but there does not seem to be a simple way to have the GridViews retrieve this custom data on a PostBack.

Is there any way to actually achieve what I am trying to do with ASP.NET? It seems like a simple requirement to have a totals row at the bottom of each GridView.

Thanks in advance for any help.

Was it helpful?

Solution 3

OK, the way I ended up solving this was with JQuery. If anybody else is facing a similar problem, remember that the totals must be calculated when the DOM is ready, as well as at the end of any postback. To handle the postback situation, you can just call the Javascript on the client using ScriptManager.RegisterStartupScript().

Again, I had four GridViews in my circumstance, but I'll just show the JQuery code for one of them:

$(document).ready(function () {
    setTotals();
});

function setTotals() {

    var totalHours = getBillableHoursTotal();
    if (isNaN(totalHours)) totalHours = '...editing';
    $('#spanBillableHoursTotal').html(totalHours);

    //... etc.
}

function getBillableHoursTotal() {
    var total = 0.0;
    var rows = $('table[id*="_gvWorkerHours"] tr.RegularRows');
    $(rows).each(function () {
        total = total + parseFloat($(this).children('td').children('span[id*="lblHours"]').html()); 
    });
    return total;
}

And for the C# on the code behind:

protected void Page_Load(object sender, EventArgs e)
{
    // ... preceeding Page Load code

    if (IsPostBack)
    {
        ScriptManager.RegisterStartupScript(this, this.GetType(), "?", "setTotals()", true);
    }
}

OTHER TIPS

What if you try creating the dynamic row using the gridView.OnPreRender event instead of the gridView.RowDataBound event. This way your data you need to calculate your dynaimic row results is available but the html has not been sent to the web browser yet. Please post some more code so we can provide more insight into fixing your issue.

As recommended, I tried putting the code to create the totals row in the PreRender event rather than the RowDataBound event. This seemed to work, except that it broke all of the commands for the GridView that it was used on. It appears that manually changing the GridView disrupts its automatic behavior.

protected void gvWorkerHours_PreRender(object sender, EventArgs e)
{
    double total = 0;
    int numColumns = gvWorkerHours.Columns.Count;
    int hoursIndex = 4; //5th column, 0-based
    int rowIndex = gvWorkerHours.Rows.Count + 1;

    foreach (GridViewRow row in gvWorkerHours.Rows)
    {
        if (row.RowType == DataControlRowType.DataRow)
        {
            Label label = (Label)row.FindControl("lblHours");
            total += Convert.ToDouble(label.Text);
        }
    }

    CreateTotalRow((Table)gvWorkerHours.Rows[0].Parent, rowIndex, total, hoursIndex, numColumns);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top