Fighting the SPGridView contol

hOne piece of SharePoint toolbox, rarely used, and not well known, is a SPGridView control, a SharePoint wrapper over a standard .NET GridView control.

Since the SPGridView is not well documented, and implementing standard functionality with it is somewhat painful, I have developed a wrapper around it, which can be used to bind the control at runtime by using bindable data sources.

This piece od code is from my upcoming session on Microsoft WinDays 2008.

Hope it helps.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Diagnostics;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Data;
using System.Web.UI;
using System.Collections;

namespace Daenet.SharePointApi
{

#region DaenetSPGridView
///

/// Daenet’s override of the SPGridView control, which handles some default SPGridView’s bugs, and adds some standard behaviours
///

public class DaenetSPGridView : SPGridView
{
#region public properties

///

/// Fields which are displayed in grid, comma separated
///

public string FieldsToDisplay { get; set; }

///

/// field headers
///

public string FieldsHeaders { get; set; }

///

/// fields on which sorting is enabled
///

public string SortFields { get; set; }

///

/// Field which will display the menu
///

public string MenuField { get; set; }

///

/// Grid data source
///

public object GridDataSource { get; set; }

#endregion

#region private fields
///

/// Array of column names
///

string[] m_ColumnNames;

///

/// Array of Column headers
///

string[] m_HeaderNames;

#endregion

#region constructor
///

/// Initialize new DaenetSPGrid
///

/// ID of the newly created grid /// Row alternate color – leave empty if no alternate color /// DataSource object we want to use as the grid source /// Fields we want to display in grid, comma separated /// Headers of the fields we want to display /// Fields we want to enable sorting onto, comma separated /// Fields we want to enable filtering onto, comma separated, in the “FilterDataFields” mode /// Do we want to enable paging? 0 – no, n-number of the rows per page /// Set the grouping field if we want to enable grouping public DaenetSPGridView(string gridName, Color alternateColor, object gridDataSource, string displayFields, string fieldHeaders, string sortFields, string filteredFields, int pageSize, string groupingField, string menuField)
{
// Set basic gridview properties
this.ID = gridName;
this.AutoGenerateColumns = false;
if (alternateColor != null) this.AlternatingRowStyle.BackColor = alternateColor;

//set properties
GridDataSource = gridDataSource;
FieldsToDisplay = displayFields;
FieldsHeaders = fieldHeaders;
SortFields = sortFields;
MenuField = menuField;

//generate grid coumns
generateColumns();

//
//set filtering
if (!string.IsNullOrEmpty(filteredFields))
{
// Set properties for filtering
this.AllowFiltering = true;
this.FilterDataFields = filteredFields;
this.FilteredDataSourcePropertyName = “FilterExpression”;
this.FilteredDataSourcePropertyFormat = “{1} LIKE ‘{0}'”;

this.RowDataBound += new GridViewRowEventHandler(gridView_RowDataBound);
}

if (!string.IsNullOrEmpty(sortFields))
{
this.AllowSorting = true;
}

if (pageSize != 0)
{
// Set properties for paging
this.PageSize = pageSize;
this.AllowPaging = true;
this.PagerStyle.HorizontalAlign = HorizontalAlign.Right;
}

if (!string.IsNullOrEmpty(groupingField))
{
//AllowGrouping=”true” AutoGenerateColumns=”false” GroupField=”StateName”
//AllowGroupCollapse=”true” DisplayGroupFieldName=”false”>
this.AllowGrouping = true;
this.GroupField = groupingField;
this.AllowGroupCollapse = true;
this.DisplayGroupFieldName = true;

}
}
#endregion

#region public methods

public void SetColumnMenu(string columnName)
{
try
{
//
//TODO:
//1 – item Click – UrlFields
//2 – item Click – UrlFormat

//
//first hide the original column
DataControlField originalColumn = findColumnFromHeaderText(getHeaderTextFromColumnId(columnName));
string originalHeaderText = originalColumn.HeaderText;
originalColumn.HeaderText += “-OLD”;
originalColumn.Visible = false;

//
// Replace the Original coloumn with a shiny menu
SPMenuField colMenu = new SPMenuField();
colMenu.HeaderText = originalHeaderText;
colMenu.TextFields = columnName;
colMenu.MenuTemplateId = columnName + “Menu”;
colMenu.NavigateUrlFields = “ID,Title”;
colMenu.NavigateUrlFormat = “do.aspx?p={0}&q={1}”;
colMenu.TokenNameAndValueFields = “EDIT=ID,NAME=Title”;
colMenu.SortExpression = columnName;

//
//generate dropdown
MenuTemplate presenterListMenu = new MenuTemplate();
presenterListMenu.ID = columnName + “PresenterListMenu”;

//
//generate dropdownitems
MenuItemTemplate biogMenu = new MenuItemTemplate(
“Read Biography”, “/_layouts/images/EawfNewUser.gif”);
biogMenu.ClientOnClickNavigateUrl = “do.aspx?this=%EDIT%&that=%NAME%”;
presenterListMenu.Controls.Add(biogMenu);

MenuItemTemplate broadcastMenu = new MenuItemTemplate(
“Recent Broadcasts”, “/_layouts/images/ICWM.gif”);
broadcastMenu.ClientOnClickNavigateUrl = “do.aspx?this=%EDIT%&that=%NAME%”;
presenterListMenu.Controls.Add(broadcastMenu);

//separator
MenuSeparatorTemplate sepMenu = new MenuSeparatorTemplate();
presenterListMenu.Controls.Add(sepMenu);

MenuItemTemplate favMenu = new MenuItemTemplate(
“Add to Favorites”, “/_layouts/images/addtofavorites.gif”);
favMenu.ClientOnClickNavigateUrl = “do.aspx?this=%EDIT%&that=%NAME%”;
presenterListMenu.Controls.Add(favMenu);

//add presenter menu to the controls collection
this.Controls.Add(presenterListMenu);

//add column with menu temlae
this.Columns.Add(colMenu);

}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
throw;
}
}

///

/// Sets the column width – doesn’t work
///

/// /// public void SetColumnWidth(string columnName, Unit widthUnit)
{
try
{

DataControlField column = findColumnFromHeaderText(getHeaderTextFromColumnId(columnName));
column.ControlStyle.Width = widthUnit;

}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
throw;
}
}
#endregion

#region private methods
///

/// Generate column names
///

private void generateColumns()
{
try
{
m_ColumnNames = FieldsToDisplay.Split(“,”.ToCharArray());
m_HeaderNames = FieldsHeaders.Split(“,”.ToCharArray());

List sortFieldsList = SortFields.Split(“,”.ToCharArray()).ToList();

string menuFieldName = “”;
string[] menuFieldInfo = new string[]{};
if (!string.IsNullOrEmpty(MenuField))
{
menuFieldInfo = MenuField.Split(“,”.ToCharArray());
menuFieldName = menuFieldInfo[0];
}

//retrieve underlaying data from the DataSource
//IEnumerable underlayingDataTable = ((IEnumerable)(((ObjectDataSource)(GridDataSource)).Select()));
IEnumerable enumerableData = ((IEnumerable)(((ObjectDataSource)(GridDataSource)).Select()));

//
//iterate through the columns we want to display
for (int i = 0; i < m_ColumnNames.Length; i++) { if (m_ColumnNames[i] != menuFieldName) { BoundField colName = new BoundField(); colName.DataField = m_ColumnNames[i]; colName.HeaderText = m_HeaderNames[i]; if (sortFieldsList.Contains(m_ColumnNames[i])) colName.SortExpression = m_ColumnNames[i]; this.Columns.Add(colName); } else { /* *

* */
SPMenuField colName = new SPMenuField();
colName.NavigateUrlFields = “ID”;
colName.NavigateUrlFormat = menuFieldInfo[1];//”StoreManager.aspx?ID={0}
colName.TextFields = m_ColumnNames[i];
colName.HeaderText = m_HeaderNames[i];
colName.TokenNameAndValueFields = “ID=ID”;
colName.MenuTemplateId = menuFieldInfo[2];
this.Columns.Add(colName);
}
}
}
catch (Exception ex)
{
throw new ApplicationException(“Formatted SharePoint Grid Exception”, ex);
}
}

/*
///

/// Generate column names
///

private void generateColumns()
{
try
{
m_ColumnNames = FieldsToDisplay.Split(“,”.ToCharArray());
m_HeaderNames = FieldsHeaders.Split(“,”.ToCharArray());

List sortFieldsList = SortFields.Split(“,”.ToCharArray()).ToList();

//retrieve underlaying data from the DataSource
DataTable underlayingDataTable = ((DataView)(((ObjectDataSource)(GridDataSource)).Select())).Table;

//
//iterate through the columns we want to display
for (int i = 0; i < m_ColumnNames.Length; i++) { DataColumn col = underlayingDataTable.Columns[m_ColumnNames[i]]; Type colType = col.DataType; // //string and numeric fields if (colType == typeof(string) || colType == typeof(int) || colType == typeof(double)) { BoundField colName = new BoundField(); colName.DataField = m_ColumnNames[i]; colName.HeaderText = m_HeaderNames[i]; if (sortFieldsList.Contains(m_ColumnNames[i])) colName.SortExpression = m_ColumnNames[i]; this.Columns.Add(colName); } // //checkbox fields if (colType == typeof(bool)) { TemplateField colName = new TemplateField(); colName.HeaderText = m_HeaderNames[i]; colName.ItemTemplate = new CheckBoxItemTemplate(ListItemType.Item, m_ColumnNames[i]); if (sortFieldsList.Contains(m_ColumnNames[i])) colName.SortExpression = m_ColumnNames[i]; this.Columns.Add(colName); } } } catch (Exception ex) { throw new ApplicationException("Formatted SharePoint Grid Exception", ex); } } */ ///

/// Sets the filter icon on the filtered field
/// We catch the gridView_RowDataBound event, check if it is a header row
/// and if it is header row, set the icon on the cell of the corresponding column
///

/// /// private void gridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
try
{
//first check if it is a header row
if ((sender != null) && (e.Row.RowType == DataControlRowType.Header))
{
//get the filter field name (ID of the field)
string strFilteredColumn = ((DaenetSPGridView)sender).FilterFieldName;

//get the header name from the field name
string headerText = getHeaderTextFromColumnId(strFilteredColumn);

//set the icon
if (!string.IsNullOrEmpty(headerText))
SetGridViewFilterIcon(headerText, e.Row);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}

///

/// Sets the Filter icon on the filtered column
///

/// Header text of the column we are setting the icon to /// Grid row of the cell we want to set the icon to – has to be the header row public void SetGridViewFilterIcon(string columnHeaderText, GridViewRow row)
{
try
{

if ((!string.IsNullOrEmpty(columnHeaderText)) && (row != null))
{
//
//find an index of the column we are searching for
int column = findColumnIndexFromHeaderText(columnHeaderText);

//
//if column index found, set the icon
if (column != -1)
{
addIconToCell(row.Cells[column], “filterIcon”, “/_layouts/images/ewr093.gif”, ImageAlign.Left, “2px”, “”, “”, “”);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
throw;
}
}

///

/// Finds a DataControlField (column) in the SPGridView columns based on column’s header text –
/// since we do not have IDs there.
///

/// Header text of the column we are searching for /// DataControlField of the column or null if not found
private DataControlField findColumnFromHeaderText(string headerText)
{
for (int iIndex = 0; iIndex < this.Columns.Count; iIndex++) { DataControlField oField = this.Columns[iIndex]; if (oField.HeaderText == headerText) return oField; } return null; } ///

/// Finds a DataControlField (column) index (integer) in the SPGridView columns based on column’s header text –
/// since we do not have IDs there.
///

/// Header text of the column we are searching for /// Integer index of the column or -1 if not found
private int findColumnIndexFromHeaderText(string headerText)
{
for (int iIndex = 0; iIndex < this.Columns.Count; iIndex++) { DataControlField oField = this.Columns[iIndex]; if (oField.HeaderText == headerText) return iIndex; } return -1; } ///

/// Gets the header text from the coulumn id – used for finding columns…
///

/// column Id we are lookinf for /// Header text of the column
private string getHeaderTextFromColumnId(string columnId)
{
try
{
int columnPosition = m_ColumnNames.ToList().IndexOf(columnId);
if (columnPosition >= 0)
{
return m_HeaderNames[columnPosition];
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
throw;
}
}

///

/// Adds an icon to the cell
///

/// /// private static void addIconToCell(TableCell cellToAddIcon, string iconId, string imageUrl, ImageAlign align, string marginTop, string marginLeft, string marginRight, string marginBottom)
{
System.Web.UI.WebControls.Image oFilterIcon = new System.Web.UI.WebControls.Image();
oFilterIcon.ImageUrl = imageUrl;
oFilterIcon.ImageAlign = align;

if (!string.IsNullOrEmpty(marginTop)) oFilterIcon.Style[System.Web.UI.HtmlTextWriterStyle.MarginTop] = marginTop;
if (!string.IsNullOrEmpty(marginLeft)) oFilterIcon.Style[System.Web.UI.HtmlTextWriterStyle.MarginLeft] = marginLeft;
if (!string.IsNullOrEmpty(marginRight)) oFilterIcon.Style[System.Web.UI.HtmlTextWriterStyle.MarginRight] = marginRight;
if (!string.IsNullOrEmpty(marginBottom)) oFilterIcon.Style[System.Web.UI.HtmlTextWriterStyle.MarginBottom] = marginBottom;

oFilterIcon.ID = iconId;

Panel oPanel = new Panel();
oPanel.Controls.Add(oFilterIcon);

//”2px”
//ImageAlign.Left
//”/_layouts/images/ewr093.gif”
//GridViewRow oRow, int iIndex
//oRow.Cells[iIndex] cellToAddIcon.Controls.Add(oPanel);
}

#endregion

#region overrides
///

/// Override LoadControlState to Rebind if datasource not there
///

/// protected override void LoadControlState(object savedState)
{
base.LoadControlState(savedState);
if (this.DataSource == null)
{
this.DataBind();
}

}
#endregion

}
#endregion

//
//Item Template classes
#region Item template classes

///

/// Check Box Item Template Class
///

public class CheckBoxItemTemplate : ITemplate
{

///

/// Item Type – determines if we are dealing with the header or list item
///

private ListItemType m_ItemType;

///

/// Name of the column – field we are setting the template for
///

private string m_FieldName;

///

/// Constructor
///

/// Item Type – to determine if the item is header or list item /// Field name we are setting public CheckBoxItemTemplate(ListItemType itemType, string fieldName)
{
m_ItemType = itemType;
m_FieldName = fieldName;
}

public void InstantiateIn(System.Web.UI.Control controlContainer)
{
if (m_ItemType == ListItemType.Item || m_ItemType == ListItemType.SelectedItem || m_ItemType == ListItemType.AlternatingItem)
{
System.Web.UI.WebControls.Image checkIcon = new System.Web.UI.WebControls.Image();
checkIcon.ImageAlign = ImageAlign.Middle;

checkIcon.Style[System.Web.UI.HtmlTextWriterStyle.MarginTop] = “2px”;

checkIcon.ID = “CheckIcon_” + m_FieldName;
checkIcon.Visible = true;
checkIcon.DataBinding += new EventHandler(cb_DataBinding);
controlContainer.Controls.Add(checkIcon);
}
}

///

/// Catch the databinding event of the checkbox, to set the value
///

/// /// private void cb_DataBinding(object sender, EventArgs e)
{

System.Web.UI.WebControls.Image imageSender = (System.Web.UI.WebControls.Image)(sender);
SPGridViewRow parentRow = (SPGridViewRow)(imageSender.NamingContainer);
try
{
bool cbChecked = (bool)(DataBinder.Eval(parentRow.DataItem, m_FieldName));

if (cbChecked)
{
imageSender.ImageUrl = “/_layouts/images/CHECK.GIF”;
}
else
{
imageSender.ImageUrl = “/_layouts/images/UNCHECK.GIF”;
}

}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
imageSender.ImageUrl = “/_layouts/images/UNCHECK.GIF”;
}
}
}
#endregion

}

public class OdsDataViewWrapper
{

///

/// DataView from which we want to display data
///

public static DataTable DataToDisplay { get; set; }

///

/// Object to display
///

public static IEnumerable ObjectToDisplay { get; set; }

///

/// Constructor
///

public OdsDataViewWrapper() { }

///

/// Return data from the view
///

///
public static System.Data.DataTable GetData()
{

if (DataToDisplay == null)
{
return new DataTable();
}
else
{
return DataToDisplay;
}
}

///

/// Return object data from the view
///

/// IEnumerable object
public static IEnumerable GetObjectData()
{
if (ObjectToDisplay == null)
{
List dummyStringList = new List();
dummyStringList.Add(“No”);
dummyStringList.Add(“Data”);
return dummyStringList;
}
else
{
return ObjectToDisplay;
}
}

public static int UpdateAuthor(string au_id, string au_lname, string au_fname, string state)
{
return 1;
}

///

/// Creates an ObjectDataSource object which wrapps the DataTable data
///

/// ID of the ObjectDataSource /// DataTable from which we want to display data /// ObjectDataSource with wrapped data
public static ObjectDataSource GetObjectDataSource(string odsID, DataTable dataTable)
{
DataToDisplay = dataTable;

ObjectDataSource ods = new ObjectDataSource();
ods.ID = odsID;
ods.TypeName = “Daenet.SharePointApi.OdsDataViewWrapper”;
ods.SelectMethod = “GetData”;
OdsDataViewWrapper.DataToDisplay = dataTable;

return ods;
}

///

/// Creates an ObjectDataSource object which wrapps the IEnumerable data
///

/// ID of the ObjectDataSource /// DataTable from which we want to display data /// ObjectDataSource with wrapped data
public static ObjectDataSource GetObjectDataSource(string odsID, IEnumerable objectSource)
{
ObjectToDisplay = objectSource;

ObjectDataSource ods = new ObjectDataSource();
ods.ID = odsID;
ods.TypeName = “Daenet.SharePointApi.OdsDataViewWrapper”;
ods.SelectMethod = “GetObjectData”;
OdsDataViewWrapper.ObjectToDisplay = objectSource;

return ods;
}

///

/// Checks hrom the HttpRequest if the gridview sorting has been set
///

/// HttpRequest object where we are checking if the Filter was earlier set /// SPGridView for which we are setting the Filter /// Key in the viewstate where we are storing current filter expression /// ViewState collection where we are storing the current filter expression public static bool CheckRequestForFilterExpression(HttpRequest httpRequest, DaenetSPGridView gridView, string expressionKeyName, StateBag viewState)
{
if ((httpRequest.Form[“__CALLBACKID”] == null) ||
(httpRequest.Form[“__CALLBACKPARAM”] == null) ||
(!httpRequest.Form[“__CALLBACKID”].EndsWith(gridView.ID)))
{
if (viewState[expressionKeyName] != null)
{
return true;
}
}

return false;
}

///

/// Creates a menu template field
///

/// ID of the menu template field /// MEnu template field
public static MenuTemplate GetMenuTemplateField(string templateID, string[] menuItemTexts,
string[] menuItemDescriptions, string[] menuItemImageUrls, string[] menuItemNavigateUrls )
{
/*
*


* */

MenuTemplate menuField = new MenuTemplate();
menuField.ID = templateID;

//
//add items
for (int i = 0; i < menuItemTexts.Length; i++) { MenuItemTemplate itemTemplate = new MenuItemTemplate(); itemTemplate.Text = menuItemTexts[i]; itemTemplate.ID = templateID + "_item_" + i.ToString(); itemTemplate.Description = menuItemDescriptions[i]; itemTemplate.ImageUrl = menuItemImageUrls[i]; itemTemplate.Sequence = i; itemTemplate.ClientOnClickNavigateUrl = menuItemNavigateUrls[i]; menuField.Controls.Add(itemTemplate); } return menuField; } } [/csharp]