Most discussions on data grids were for ASP.net and not in web parts and that annoyed me... so here's a few things you need to know about datagrids in web parts in SharePoint.
Firstly, the datagrid needs to be bound in CreateChildControls.
dgRecords = new DataGrid();
dgRecords.EnableViewState = true;
dgRecords.Width = new Unit("100%");
dgRecords.AutoGenerateColumns = false;
dgRecords.BorderStyle = BorderStyle.None;
dgRecords.FooterStyle.BorderStyle = BorderStyle.None;
dgRecords.HeaderStyle.CssClass = "ms-formlabel";
dgRecords.HeaderStyle.Font.Size = FontUnit.XXSmall;
dgRecords.CellPadding = 3;
dgRecords.ItemStyle.Height = 30;
dgRecords.AllowPaging = true;
dgRecords.PageSize = 25; // results returned per page
dgRecords.PagerStyle.Mode = PagerMode.NumericPages;
dgRecords.PagerStyle.HorizontalAlign = HorizontalAlign.Center;
dgRecords.PageIndexChanged += new DataGridPageChangedEventHandler(dgRecords_PageIndexChanged);
this.Controls.Add(dgRecords);
OpenSQLConnection();
SqlCommand cmd = getSqlCommand();
cmd.CommandType = CommandType.Text;
SqlDataAdapter myAdapter = new SqlDataAdapter(cmd);
dsRecords = new DataSet();
myAdapter.Fill(dsRecords, "TableName");
dv = new DataView(dsRecords.Tables["TableName"]);
HyperLinkColumn linkColumn = new HyperLinkColumn();
linkColumn.DataTextField = "fldID";
linkColumn.DataTextFormatString = "{0}";
linkColumn.DataNavigateUrlField = "fldID";
linkColumn.DataNavigateUrlFormatString = "http://your_intranet/Pages/default.aspx?ticketid={0}";
linkColumn.HeaderText = "ID#";
BoundColumn itemColumn = new BoundColumn();
itemColumn.DataField = "fldItem";
itemColumn.DataFormatString = "{0}";
itemColumn.HeaderText = "Item Ordered";
BoundColumn priceColumn = new BoundColumn();
priceColumn.DataField = "fldPrice";
priceColumn.DataFormatString = "{0}";
priceColumn.HeaderText = "Price";
BoundColumn quantityColumn = new BoundColumn();
quantityColumn.DataField = "fldQuantity";
quantityColumn.DataFormatString = "{0}";
quantityColumn.HeaderText = "Quantity Ordered";
TemplateColumn totalColumn = new TemplateColumn();
totalColumn .ItemTemplate = new TotalColumnTemplate();
dgRecords.Columns.Add(linkColumn);
dgRecords.Columns.Add(itemColumn);
dgRecords.Columns.Add(priceColumn);
dgRecords.Columns.Add(quantityColumn);
dgRecords.Columns.Add(totalColumn);
dgRecords.HeaderStyle.BackColor = Color.FromArgb(198, 226, 255);
dgRecords.HeaderStyle.ForeColor = Color.White;
dgRecords.AlternatingItemStyle.BackColor = Color.FromArgb(247, 246, 243);
dgRecords.AlternatingItemStyle.ForeColor = Color.FromArgb(33, 33, 33);
dgRecords.ItemStyle.ForeColor = Color.FromArgb(28, 47, 75);
dgRecords.DataSource = dv;
// bind only on page load
if (!Page.IsPostBack) dgRecords.DataBind();
CloseSQLConnection();
Notice that in the code above, you are using BoundColumn a lot. This is all ok and well if you are just querying the database and retrieving the values stored in the database without needing to make any calculations on returned values. If you do need to make calculations (for example, price x quantity, then you need a TemplateColumn), which complicates things a little.
Also note that we are only binding this if it isn't a postback. The reason for this is because we need to bind it to different "data" when an event happens such as column sorting or a page change. I will go through the paginations a bit later, but won't go through the column sorting until another post in the future.
So, now if you copy and paste the above code, you will find that you don't have methods for OpenSQLConnection(), getSqlCommand(), TotalColumnTemplate() and CloseSQLConnection(). The code for all but the TotalColumnTemplate() is below:
private void OpenSQLConnection()
{
connection = new SqlConnection("server=localhost;database=hwsupport;Integrated Security=SSPI");
connection.Open();
}
private void CloseSQLConnection()
{
connection.Close();
}
private SqlCommand getSqlCommand()
{
SPWeb mySite = SPContext.Current.Web;
SqlCommand cmd = new SqlCommand("SELECT * FROM TableName", connection);
return cmd;
}
For TotalColumnTemplate(), we need to create a new class. Right click on your project and add a class file. Name it TotalColumnTemplate.cs since that is what this template is referred to as in the code. Copy the following into TotalColumnTemplate.cs through VS:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Demo
{
class LightColumnTemplate : ITemplate
{
public void InstantiateIn(Control container)
{
LiteralControl ouputLiteral = new LiteralControl();
ouputLiteral.DataBinding += new EventHandler(myLiteral_DataBinding);
container.Controls.Add(ouputLiteral);
}
public void myLiteral_DataBinding(object sender, EventArgs e)
{
LiteralControl myLiteral = ((LiteralControl)(sender));
DataGridItem container = ((DataGridItem)(myLiteral.NamingContainer));
int Price = int.Parse(DataBinder.Eval((((DataGridItem)(container))).DataItem, "fldPrice").ToString());
int Quantity = int.Parse(DataBinder.Eval((((DataGridItem)(container))).DataItem, "fldQuantity").ToString());
string strExactOutput;
strExactOutput = Convert.ToString(Price*Quantity);
myLiteral.Text = strExactOutput;
}
}
}
Make sure you set the namespace to the same namespace that your base class is using.
If you deploy your project now, you will see a data grid with the values in it.
Datagrid Pagination in Web Parts
Let's assume now that you have thousands of results returned and you need to paginate.
In the first chunk of code, you'll see this:
dgRecords.AllowPaging = true;
dgRecords.PageSize = 25; // results returned per page
dgRecords.PagerStyle.Mode = PagerMode.NumericPages;
dgRecords.PagerStyle.HorizontalAlign = HorizontalAlign.Center;
dgRecords.PageIndexChanged += new DataGridPageChangedEventHandler(dgRecords_PageIndexChanged);
This bit tells the datagrid that you want to enable paging and to run the dgRecords_PageIndexChanged event when a new page is selected.
void dgRecords_PageIndexChanged(object source, DataGridPageChangedEventArgs e)
{
OpenSQLConnection();
SqlCommand cmd = getSqlCommand();
cmd.CommandType = CommandType.Text;
SqlDataAdapter myAdapter = new SqlDataAdapter(cmd);
dsRecords = new DataSet();
myAdapter.Fill(dsRecords, "TableName");
dgRecords.DataSource = dsRecords.Tables["TableName"];
DataView dv = new DataView(dsRecords.Tables["TableName"]);
try
{
dgRecords.CurrentPageIndex = e.NewPageIndex;
}
catch
{
dgRecords.CurrentPageIndex = 0;
}
dgRecords.DataBind();
CloseSQLConnection();
}
Note that the dgRecords.DataBind() needs to be run in here to ensure that the rows returned for this page is all that is displayed. If you don't bind the data, the results will not be correct.
I remember that while I was doing this, changing pages worked, but when I go back to Page 1, nothing seems to happen. The reason is because of dgRecords.DataBind(); that I had placed in the CreateChildControls override. I had it such that it was binding when a new page is selected. It should have been if (!Page.IsPostBack) dgRecords.DataBind();. It then needs to be bound in the PageIndexChanged event handler (this will apply for column sorting too).
I wasted a few hours trying to work out why I can't go back to Page 1, so hopefully this will save you the few hours I wasted trying to figure this out.
Data Grid Web Part Alternating Row Colours
This wasted me a few hours too trying to figure out how to alternate row colours in a data grid.
In the end it was fairly simple... just look at the following code:
dgRecords.HeaderStyle.BackColor = Color.FromArgb(198, 226, 255);
dgRecords.HeaderStyle.ForeColor = Color.White;
dgRecords.AlternatingItemStyle.BackColor = Color.FromArgb(247, 246, 243);
dgRecords.AlternatingItemStyle.ForeColor = Color.FromArgb(33, 33, 33);
dgRecords.ItemStyle.ForeColor = Color.FromArgb(28, 47, 75);
You can set alternating item styles background colors and foreground colors from RGB or from a predefined color. This one was really obvious, I don't know why it took me so long to figure out. Maybe I just didn't get enough sleep that day.