Sunday, February 27, 2005

Displaying 2 Field Names in 1 Column of DataGrid


In the previous articles, I used the Authors table from Pubs database to display data in the datagrid. As you can see the screenshots, the author's firstname (au_fname) and author's lastname (au_lname) are displayed in the different columns. Perhaps, you would be wondering how to combine both au_fname and au_lname fields into 1 column. Now, I will demonstrate two ways to do that. First method using new combined DataColumn, and second method using Data Binding in the ItemDataBound event. To avoid this article from becoming lengthy, I combine both methods into 1 solution. [As you will notice that there are two same columns, which have author's full names.]


In the aspx page,

<asp:datagrid id="dgrdAuthors" onItemDataBound="dgrdAuthors_ItemDataBound" .......>
<Columns>
     <asp:BoundColumn DataField="au_id" HeaderText="Author ID"/>
     <asp:BoundColumn DataField="FullName" HeaderText="Name (DataColumn)"/>;      <-- First method

      <asp:TemplateColumn HeaderText="Name(DataItem)">      <-- Second method
          <ItemTemplate>
               <asp:label id="lblFullName" runat="server"/>
          </ItemTemplate>
     </asp:TemplateColumn>

     <asp:BoundColumn DataField="phone" HeaderText="Phone"/>
     <asp:BoundColumn DataField="city" HeaderText="City"/>
     <asp:BoundColumn DataField="state" HeaderText="State"/>
</Columns>
</asp:datagrid>



In my DataGrid, there are total number of 6 columns, which 2 of columns are the same (Name(DataColumn) and Name(DataItem) columns). As you can notice, one of the columns used is TemplateColumn. This column is customizable and provide more flexibility in formatting the appearance of the displayed data. The purpose of using this TemplateColumn here is I want to customize the data (combining both au_fname and au_lname) at run time.


Definitions : TemplateColumn class | BoundColumn Class | ItemDataBound Event

See Also : Customizing DataList Items at Run Time [MSDN]


In the <script> tag or code behind ,

<script>
protected void Page_Load(Object sender, EventArgs e)
{
     if(!IsPostBack)
     {


          SqlConnection conPubs = new SqlConnection("Server=DOTNET;uid=sa;database=pubs");
          SqlDataAdapter daSelectAuthors = new SqlDataAdapter("SELECT au_id, au_fname, au_lname, phone,city,state FROM Authors",conPubs);
               DataSet dsAuthors = new DataSet();

               conPubs.Open();
               daSelectAuthors.Fill(dsAuthors,"Authors");
               conPubs.Close();

               DataColumn dcolFullName = new DataColumn();
               dcolFullName.ColumnName = "FullName";
               dcolFullName.DataType = System.Type.GetType("System.String");
               dcolFullName.Expression = "au_fname + ' ' + au_lname";

               dsAuthors.Tables["Authors"].Columns.Add(dcolFullName); // add new column to the datatable

               dgrdAuthors.DataSource = dsAuthors.Tables["Authors"].DefaultView;
               dgrdAuthors.DataBind();
          }
     }
}


private void dgrdAuthors_ItemDataBound(Object sender, DataGridItemEventArgs e)
{
     if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
     {
          Label lblFullName = (Label)e.Item.FindControl("lblFullName");

          lblFullName.Text = (((DataRowView)e.Item.DataItem)["au_fname"]).ToString() + " " +
                                          (((DataRowView)e.Item.DataItem)["au_lname"]).ToString();
     }


}
</script>



For the first method, I have created a new datacolumn, which has FullName column name and string data type. The Expression property of DataColumn can be used for calculating values from different data fields, combining columns and so on. At here, I'm combining the au_fname and au_lname fields as 1 field (column). After that, I add this new column to the DataColumnCollection of "Authors" DataTable, and then bind this DataSet to DataGrid. Eventually, the DataSet has 7 fields.


In the ItemDataBound event, I check the data rows using the If..... Statement, and for each data row, I use the FindControl() to search and locate the Label server control that is placed in the TemplateColumn there by its ID. Of course, I have to cast the returned result of the FindControl() to Label type. Let us proceed to next line, which will be a bit complicated.


The e.Item.DataItem is an object that represents the data item in the DataGrid. It can only be placed in the data rows like Item, AlternatingItem, SelectedItem and EditedItem. Whereas the DataRowView represents each displayed data row. The ((DataRowView)e.Item.DataItem)["au_fname"] is an explicit casting. Alternatively, you can change to the DataBinder.Eval(e.Item.DataItem,"au_fname"), which is the equivalent to the ((DataRowView)e.Item.DataItem)["au_fname"]. The reason I use the explicit casting is because it offers better performance than DataBinder.Eval() in avoiding the cost of reflection.

TRY it and GET it !


Definitions : DataColumn.ColumnName Property | DataColumn.DataType Property | DataColumn.Expression Property | Type Class | DataColumnCollection.Add() | DataGridItem.DataItem | DataRowView Class

See Also : Improving ASP.NET Performance [MSDN]

No comments: