Asp.net datagrid problem



  • here's an example of what I need to do:  I have a db table of students and final grades by percentage.  On a webpage I need a table with 2 columns: the names, and their statuses.  The status is just a pass/fail, that checks if the user got a 70% or better.

    The problem is that with ASP, you'd just write out the <td> and <tr> and do a check inside the status cells, but using ASP.NET's datagrid makes it a little tricky.  I don't know how to do it, since all you do is bind the data to the table, and it magically appears on your page.

    Someone's told me already that I need to pass the databind part into a function and do some sort of check there.......but since I don't know .NET well at all, I don't know the appropriate classes and methods I should be referencing.

    Any help is much appreciated.



  • There are at least two way I can think of to do this.

    1. Convert the status column in the data grid to a template column, then modify it to pass the column value to a function that will determine the status. That would look like this:

    The HTML part would look something like this:

    <asp:TemplateColumn HeaderText="Status">
    <ItemTemplate>
    <asp:Label runat="server" Text='<%# GetStatus(DataBinder.Eval(Container, "DataItem.percentage")) %>'>
    </asp:Label>
    </ItemTemplate>
    </asp:TemplateColumn>

    This would be the function:

        Public Function GetStatus(ByVal percentage As Integer) As String
            if percentage >= 70 then 
                return "Pass"
           else
               return "Fail"
          end if
       End Function

    2.  Read the data into a Datatable object, add  a new column called status, iterate through the table to set the value of status, then bind the table to a datagrid that only shows the name and status columns. This method would be preferable if you need to calculate a column based on more then one other column.

    Dan



  • Use the server-side Table control.  Just add TableRows to the Table and TableCells to the TableRows.  It works just like the old ASP table (It even renders as a table in the HTML).



  • I would handle the ItemDataBound event raised for each row:



            protected DataGrid _grid;



            protected override void OnLoad(EventArgs e)

            {

                base.OnLoad (e);

                LoadGrid();

            }



            private void LoadGrid()

            {

               
    _grid.ItemDataBound +=new DataGridItemEventHandler(_grid_ItemDataBound);



                _grid.DataSource = GenerateTestData();

                _grid.DataBind();

            }



            private void _grid_ItemDataBound(object sender, DataGridItemEventArgs e)

            {

               
    if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType ==
    ListItemType.AlternatingItem)    // not a header, etc.

                {

               
        int score =
    (int)((DataRowView)e.Item.DataItem).Row["Score"];

               
        e.Item.Cells[1].Text = score >= 70 ? "Pass" :
    "Fail";

                }           

            }



            private DataSet GenerateTestData()

            {

                DataSet ds = new DataSet();

                DataTable dt = new DataTable();

                dt.Columns.Add("StudentName", typeof(String));

                dt.Columns.Add("Score", typeof(Int32));

                dt.Rows.Add(new object[]{"Fred", 30});

                dt.Rows.Add(new object[]{"Barney", 50});

                dt.Rows.Add(new object[]{"Wilma", 100});

                dt.Rows.Add(new object[]{"Betty", 80});

                dt.Rows.Add(new object[]{"Mr. Slate", 90});

                dt.Rows.Add(new object[]{"Joe Rockhead", 10});

                ds.Tables.Add(dt);



                return ds;

            }



  • There is an even easier solution using a ternary operator: (C# Sample Code)

    <asp:TemplateColumn HeaderText="Status">
    <ItemTemplate>
    <asp:Label runat="server" Text='<%# ((int)DataBinder.Eval(Container, "DataItem.percentage") < 70 ? "Fail" : "Pass") %>'>
    </asp:Label>
    </ItemTemplate>
    </asp:TemplateColumn>



  • Because I am a big fan of self documenting code I would have named the method ConvertPercentageToPassFailString(...) or ConvertGradeToPassFailString(...)



  • @tedbilly said:

    There is an even easier solution using a ternary operator: (C# Sample Code)

    <asp:templatecolumn headertext="Status">
    <itemtemplate>
    <asp:label runat="server" text="&lt;%# ((int)DataBinder.Eval(Container, &quot;DataItem.percentage&quot;) &lt; 70&nbsp;? &quot;Fail&quot; : &quot;Pass&quot;) %&gt;">
    </asp:label>
    </itemtemplate>
    </asp:templatecolumn>



  • ugh...so much for quoting HTML code...



    My comment was:

    That works, but my preference is to keep as much logic as possible in the codebehind or other classes.

    IMO, embedded server-side code in the HTML makes it harder to
    understand and maintain...that whole "separation of presentation from
    business logic" thing.




  • I too am a big fan of code behind but not in all cases.  For example which is easier to read?  Also, sometimes it is advantageous to be able to change the page without recompiling.  IE: Changing the message to 'Failing grade at 68%' or something similar.  The advantage to the late binding or formating is rapid change.



  • @tedbilly said:

    I too am a big fan of code behind but not in
    all cases.  For example which is easier to read?  Also,
    sometimes it is advantageous to be able to change the page without
    recompiling.  IE: Changing the message to 'Failing grade at 68%'
    or something similar.  The advantage to the late binding or
    formating is rapid change.



    I think the codebehind is almost always easier to read. You can
    organize code in methods & regions, or move it to another class or
    assembly to maximize reuse.



    I work on some pretty large applications, so matters of organization
    are forefront in my mind. This example is trivial, so I wouldn't make a
    fuss about it either way, but I've never worked on a project where a
    change simply involved an HTML page; a recompile is a given.



  • Why not just add a calculated column to the DataTable and bind it?  I try to avoid OnItemDataBound when I can because it can make the code a bit hard to follow, TemplateColumns are sometimes a little too hidden, and the code doesn't get compiled until the page is hit.  It just adds one more thing to test, I like it when the compiler does that for me.  Of course the expression in a calculated DataColumn doesn't get validated until the DataTable gets populated, but it would likely be in a data access class and be hit from more pages.



  • @tedbilly said:

    I too am a big fan of code behind but not in all cases.  For example which is easier to read?  Also, sometimes it is advantageous to be able to change the page without recompiling.  IE: Changing the message to 'Failing grade at 68%' or something similar.  The advantage to the late binding or formating is rapid change.

    The advantage of late binding is that you'll get to know the debugger very very well.

    If you want rapid change, put the questionable rules in a database or a config file.  Besides, rapid change is overrated for a web app.  That pesky state stuff does nasty things to existing users if you make a change.  Even if you manage state out-of-process there is still the issue of state stored in the old version not being relevant to the new version, or even worse, the state says you've passed xyz checks, but they've changed and you move on to process pdq with bad data.

    With a real web app, you'd want to pull one server out of a farm, update it, and let new users connect to it.  Then when all the sessions have expired on others servers in the farm, take them out and update them.  Rinse and repeat.  If you have only one server, then all new sessions get a maintenance message and the update is done when all of the current sessions end.


Log in to reply