SharePoint has a little known web control called SPGridView.  This control can give you a SharePoint list-like view of data from any data source you can bind to (from a database or other sources).  It also has features to sort, filter and group the data in much the same way users can with SharePoint lists.  For more information on how to use this, Paul Robinson has two great posts: SPGridView and SPMenuField: Displaying custom data through SharePoint lists Part 1 and Part 2.  In addition, Robert Fridén has a good post on Filtering with SPGridView.  However, there are two bugs that can be annoying and cause confusion to users:

  • When both filtering and grouping are enabled, the first group title row sometimes disappears.
  • When both sorting and filtering are enabled, the sort indicator arrow image will sometimes appear in the wrong column.

Working around either of these bugs requires you to create your own web control based on SPGridView and overriding the CreateChildControls method.  Here is the full code listing for the solution to work around both bugs.





using System;

using System.Collections.Generic;

using System.Globalization;

using System.Reflection;

using System.Web.UI;

using System.Web.UI.WebControls;



using Microsoft.SharePoint.WebControls;



namespace AdventuresInConsulting

{

public class GridViewControl : SPGridView

{

protected override int CreateChildControls(System.Collections.IEnumerable dataSource, bool dataBinding)

{

// Due to a bug in SPDataView, we need to reset the private field previousGroupFieldValue so the

// first grouping row title will consistently show up

FieldInfo fieldInfo = typeof(SPGridView).GetField("previousGroupFieldValue",

BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

if (fieldInfo != null)

fieldInfo.SetValue(this, null);



int returnValue = base.CreateChildControls(dataSource, dataBinding);



// Fix up a bug in the SPGridView where it is putting the sort arrow indicator on the wrong

// column if AllowFiltering is true

if (this.AllowFiltering && this.HeaderRow != null && !string.IsNullOrEmpty(this.SortExpression))

{

List<string> filterFields = new List<string>(this.FilterDataFields.Split(','));

if (this.AllowGrouping) filterFields.Insert(0, string.Empty);



for (int i = 0; i < this.HeaderRow.Cells.Count; i++)

{

DataControlFieldHeaderCell cell = this.HeaderRow.Cells[i] as DataControlFieldHeaderCell;

if (cell != null)

{

// Find the sort image control

Image sortImage = null;

foreach (Control c in cell.Controls)

{

sortImage = c as Image;

if (sortImage != null && sortImage.ImageUrl == this.SortDirectionImageUrl)

break;

}



// If this field is filterable, make sure the menu image is correct

if (i < filterFields.Count && filterFields[i].Trim() != string.Empty)

{

// If there is also an image control, remove it (we'll add it back in the right place)

if (sortImage != null) cell.Controls.Remove(sortImage);



// Find the menu control and add or remove the image in there as needed

foreach (Control c in cell.Controls)

{

if (c is Microsoft.SharePoint.WebControls.Menu)

{

Microsoft.SharePoint.WebControls.Menu menu = c as Microsoft.SharePoint.WebControls.Menu;

if (this.SortExpression.Equals(this.Columns[i].SortExpression, StringComparison.InvariantCultureIgnoreCase))

{

// Make sure it has the sort indicator

menu.RightImageUrl = this.SortDirectionImageUrl;

menu.ImageTextSpacing = new Unit("2px", CultureInfo.InvariantCulture);

}

else if (menu.RightImageUrl == this.SortDirectionImageUrl)

{

// Make sure it doesn't have the sort indicator

menu.RightImageUrl = null;

menu.ImageTextSpacing = Unit.Empty;

}

}

}

}

else

{

if (this.SortExpression.Equals(this.Columns[i].SortExpression, StringComparison.InvariantCultureIgnoreCase))

{

// Make sure there is a sort image

if (sortImage == null)

{

sortImage = new Image();

sortImage.ImageUrl = this.SortDirectionImageUrl;

sortImage.Style[HtmlTextWriterStyle.MarginLeft] = "2px";

cell.Controls.Add(sortImage);

}

}

else if (sortImage != null)

{

// Remove the sort image if it exists

cell.Controls.Remove(sortImage);

}

}

}

}

}



return returnValue;

}

}

}