SharePoint OOTB grid view allows us
to perform multiple filtering and sorting. But when you create a custom
solution with SPGridview then implementing multi column filter and sorting is a
bit tricky. After a good amount of goggling I found two reliable solutions.
Below are two links that helped me as
solution.
I used 2nd link in my
solution. Let’s implement an example. Add below code to your designer page.
Lets start creating an demo.
1. Create a new SharePoint Project.
2. Add a visual webpart into it.
3. Add html code to create
SPGridview.(you can take below code as well)
<SharePoint:SPGridView runat="server" ID="gvDemo" AutoGenerateColumns="false" DataSourceID="gridds"
AllowFiltering="true" AllowSorting="true" FilteredDataSourcePropertyName="FilterExpression"
HeaderStyle-HorizontalAlign="Left" EnableTheming="true" OnSorting="gvDemo_Sorting" OnRowDataBound="gvDemo_RowDataBound" EnableViewState="false"
PagerSettings-Position="Bottom" ShowHeader="true">
<Columns>
<asp:BoundField HeaderText="Title" DataField="Title" SortExpression="Title" />
<asp:BoundField HeaderText="Field1" DataField="Field1" SortExpression="Field1" />
<asp:BoundField HeaderText="Field2" DataField="Field2" SortExpression="Field2" />
<asp:BoundField HeaderText="Field3" DataField="Field3" SortExpression="Field3" />
</Columns>
</SharePoint:SPGridView>
<asp:ObjectDataSource ID="gridds" runat="server" OnObjectCreating="gridds_ObjectCreating" EnableViewState="false"
SelectMethod="GetDataSourceApprove" ></asp:ObjectDataSource>
|
We need to create a set of properties
like below, this properties contain code that will handle viewstate values of
filter and sort expressions.
#region Properties
private char[] _sortingSeparator = { ',' };
private string[] _filterSeparator = { "AND" };
private const string AppViewStateFilterExp = "ApproveFilterExpression";
private const string AppViewStateSortExp = "ApproveSortExpression";
private const string ApproveGridViewID = "gvDemo";
private const string FilteredDataSourcePropertyName = "FilterExpression";
private const string FilteredDataSourcePropertyFormat = "convert({1}, 'System.String')
like '{0}'";
private static string CallBackGridID;
private static string CallBackGridParam;
private const string FilterDataFields = "Title,Field1,Field2,Field3";
string FilterExpressionApprove
{
get
{
if (ViewState[AppViewStateFilterExp] == null)
{ ViewState[AppViewStateFilterExp] = ""; }
return (string)ViewState[AppViewStateFilterExp];
}
set
{
string thisFilterExpression = "(" + value.ToString() + ")";
List<string> fullFilterExpression = new List<string>();
if (ViewState[AppViewStateFilterExp] != null)
{
string[] fullFilterExp =
ViewState[AppViewStateFilterExp].ToString().Split(_filterSeparator, StringSplitOptions.RemoveEmptyEntries);
fullFilterExpression.AddRange(fullFilterExp);
//if the filter is gone
expression already exist?
int index = fullFilterExpression.FindIndex(s =>
s.Contains(thisFilterExpression));
if (index == -1)
{ fullFilterExpression.Add(thisFilterExpression); }
}
else
{
fullFilterExpression.Add(thisFilterExpression);
}
//Create final Filter
expression
string filterExp = string.Empty;
filterExp = string.Join(" AND ",
fullFilterExpression.ToArray());
ViewState[AppViewStateFilterExp] = filterExp;
}
}
string SortExpressionApprove
{
get
{
if (ViewState[AppViewStateSortExp] == null)
{ ViewState[AppViewStateSortExp] = ""; }
return (string)ViewState[AppViewStateSortExp];
}
set
{
string[] thisSE = value.ToString().Split(' ');
string thisSortExpression = thisSE[0];
List<string> fullSortExpression = new List<string>();
if (ViewState[AppViewStateSortExp] != null)
{
string[] fullSortExp =
ViewState[AppViewStateSortExp].ToString().Split(_sortingSeparator);
fullSortExpression.AddRange(fullSortExp);
//does the sort expression
already exist?
int index =
fullSortExpression.FindIndex(s => s.Contains(thisSortExpression));
if (index >= 0)
{
string s = string.Empty;
if (value.ToString().Contains("DESC"))
{ s = value.ToString(); }
else
{
s =
fullSortExpression[index];
if (s.Contains("ASC"))
{ s = s.Replace("ASC", "DESC"); }
else
{ s = s.Replace("DESC", "ASC"); }
}
//reset the sort direction
fullSortExpression[index] = s;
}
else
{
if (value.ToString().Contains("DESC"))
{
fullSortExpression.Add(value.ToString()); }
else
{
fullSortExpression.Add(thisSortExpression + " ASC"); }
}
}
else
{
if (value.ToString().Contains("DESC"))
{ fullSortExpression.Add(value.ToString()); }
else
{ fullSortExpression.Add(thisSortExpression + " ASC"); }
}
//loop through the
list<T> and serialize to string
string sortExp = string.Empty;
fullSortExpression.ForEach(s => sortExp += s);
sortExp = sortExp.Replace(" ASC", " ASC,");
sortExp = sortExp.Replace(" DESC", " DESC,");
ViewState[AppViewStateSortExp] = sortExp.Remove(sortExp.LastIndexOf(','));
}
}
#endregion
|
Now we need to add a Javascript file that
will handle images that are to be shows as when user applies filter and
sorting. You can place javascript file in Layouts folder like below.
That’s it now just loads script in
page load method like below.
ScriptLink.Register(this.Page, "/_layouts/MultiColumnFilterGridViewDemo/Custom_spgridview.js", false);
|
We have replaces default Select
Method of Object Data Source from “SelectGridData” to “GetDataSourceApprove” .
protected void Page_Load(object sender, EventArgs e)
{
gridds.FilterExpression = FilterExpressionApprove;
ScriptLink.Register(this.Page, "/_layouts/MultiColumnFilterGridViewDemo/Custom_spgridview.js", false);
//pass the SortExpression to the select method
gridds.SortParameterName = "SortExpression";
//Nice (!important) -> this resets the dropdown
options for other columns after a filter is selected
gridds.FilterExpression = FilterExpressionApprove;
gvDemo.FilterDataFields = FilterDataFields;
gvDemo.FilteredDataSourcePropertyFormat =
FilteredDataSourcePropertyFormat;
gridds.TypeName = this.GetType().AssemblyQualifiedName;
gvDemo.DataSourceID = gridds.ID;
gvDemo.DataBind();
}
protected void gvDemo_Sorting(object sender,
System.Web.UI.WebControls.GridViewSortEventArgs e)
{
string sDir = e.SortDirection.ToString();
sDir = sDir == "Descending" ? "
DESC" : "";
SortExpressionApprove = e.SortExpression + sDir;
e.SortExpression = SortExpressionApprove;
//keep the filter
if (!string.IsNullOrEmpty(FilterExpressionApprove))
gridds.FilterExpression = FilterExpressionApprove;
}
protected void gridds_ObjectCreating(object sender, System.Web.UI.WebControls.ObjectDataSourceEventArgs e)
{
e.ObjectInstance = this;
}
//function to clean the Filter Set already.
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
//set call back
HttpRequest oRequest = HttpContext.Current.Request;
if (oRequest.Form["__CALLBACKID"] != null)
CallBackGridID = oRequest.Form["__CALLBACKID"].ToString();
//Enable icon for correct column
if (oRequest.Form["__CALLBACKPARAM"] != null)
CallBackGridParam = oRequest.Form["__CALLBACKPARAM"].ToString().Replace("__SPGridView__;", "");
//Clear Filter code
if
(Context.Request.Form["__EVENTARGUMENT"] != null &&
Context.Request.Form["__EVENTARGUMENT"].Contains("__ClearFilter__") &&
CallBackGridID.Contains(gvDemo.ID) &&
ViewState[AppViewStateFilterExp] != null)
{
List<string> finalFilterExpression = new List<string>();
string[] seperatorHere = { " AND " };
string[] currentFilterExp =
ViewState[AppViewStateFilterExp].ToString().Split(seperatorHere, StringSplitOptions.RemoveEmptyEntries);
//code to remove the filter
only from the particular column
foreach (string exp in currentFilterExp)
{
if (!exp.Contains(CallBackGridParam))
finalFilterExpression.Add(exp);
}
ViewState.Remove(AppViewStateFilterExp);
ViewState[AppViewStateFilterExp] = String.Join(" AND ", finalFilterExpression.ToArray());
}
}
protected void gvDemo_RowDataBound(object sender, System.Web.UI.WebControls.GridViewRowEventArgs e)
{
if (sender == null || e.Row.RowType != DataControlRowType.Header)
{ return; }
SPGridView grid = sender as SPGridView;
// Show icon on filtered and sorted columns
for (int i = 0; i <
grid.Columns.Count; i++)
{
DataControlField field = grid.Columns[i];
//Necessary
if (String.IsNullOrEmpty(field.SortExpression))
continue;
if
(FilterExpressionApprove.Contains(field.SortExpression) &&
!string.IsNullOrEmpty(FilterExpressionApprove))
{
PlaceHolder panel = HeaderImages(field, "/_layouts/images/filter.gif");
e.Row.Cells[i].Controls[0].Controls.Add(panel);
}
else if
(SortExpressionApprove.Contains(field.SortExpression))
{
string url = sortImage(field);
PlaceHolder panel = HeaderImages(field, url);
e.Row.Cells[i].Controls[0].Controls.Add(panel);
}
}
}
private PlaceHolder HeaderImages(DataControlField field, string imageUrl)
{
Image filterIcon = new Image();
filterIcon.ImageUrl = imageUrl;
filterIcon.Style[HtmlTextWriterStyle.MarginLeft] = "2px";
Literal headerText = new Literal();
headerText.Text = field.HeaderText;
PlaceHolder panel = new PlaceHolder();
panel.Controls.Add(headerText);
//add the sort icon if needed
if
(FilterExpressionApprove.Contains(field.SortExpression) &&
SortExpressionApprove.Contains(field.SortExpression))
{
string url = sortImage(field);
Image sortIcon = new Image();
sortIcon.ImageUrl = url;
sortIcon.Style[HtmlTextWriterStyle.MarginLeft] = "1px";
panel.Controls.Add(sortIcon);
//change the left margin to
1
filterIcon.Style[HtmlTextWriterStyle.MarginLeft] = "1px";
}
panel.Controls.Add(filterIcon);
return panel;
}
private string sortImage(DataControlField field)
{
string url = string.Empty;
string[] fullSortExp = SortExpressionApprove.Split(_sortingSeparator);
List<string> fullSortExpression = new List<string>();
fullSortExpression.AddRange(fullSortExp);
//does the sort expression already exist?
int index =
fullSortExpression.FindIndex(s => s.Contains(field.SortExpression));
if (index >= 0)
{
string s = fullSortExpression[index];
if (s.Contains("ASC"))
{ url = "_layouts/images/sortup.gif"; }
else
{ url = "_layouts/images/sortdown.gif"; }
}
return url;
}
void buildFilterView(string filterExp)
{
string lastExp = filterExp;
if
(lastExp.Contains("AND"))
{
if (lastExp.Length < lastExp.LastIndexOf("AND") + 4)
{ lastExp = lastExp.Substring(lastExp.LastIndexOf("AND") + 4); }
else
{ lastExp = string.Empty; }
}
//update the filter
if (!string.IsNullOrEmpty(lastExp))
{ FilterExpressionApprove = lastExp; }
//reset object dataset filter
if (!string.IsNullOrEmpty(FilterExpressionApprove))
{ gridds.FilterExpression = FilterExpressionApprove; }
//set the filterFieldName parameter with multi
value ";;;;" used in the JsCode
if (!string.IsNullOrEmpty(FilterExpressionApprove))
setFilterFiledNameAttribute(FilterExpressionApprove, _filterSeparator,
gvDemo);
}
public DataTable GetDataSourceApprove(string SortExpression)
{
DataTable dt = new DataTable();
// To Do:
dt.Columns.Add("Title");
dt.Columns.Add("Field1");
dt.Columns.Add("Field2");
dt.Columns.Add("Field3");
DataRow dr;
dr = dt.NewRow(); dr["Title"] = "A1"; dr["Field1"] = "B3"; dr["Field2"] = "C3"; dr["Field3"] = "D3"; dt.Rows.Add(dr);
dr = dt.NewRow(); dr["Title"] = "A1"; dr["Field1"] = "B1"; dr["Field2"] = "C1"; dr["Field3"] = "D1"; dt.Rows.Add(dr);
dr = dt.NewRow(); dr["Title"] = "A1"; dr["Field1"] = "B1"; dr["Field2"] = "C1"; dr["Field3"] = "D2"; dt.Rows.Add(dr);
dr = dt.NewRow(); dr["Title"] = "A5"; dr["Field1"] = "B2"; dr["Field2"] = "C2"; dr["Field3"] = "D5"; dt.Rows.Add(dr);
dr = dt.NewRow(); dr["Title"] = "A4"; dr["Field1"] = "B2"; dr["Field2"] = "C2"; dr["Field3"] = "D4"; dt.Rows.Add(dr);
//Your Code to fill DataTable
#region Custom Sorting on the
DataTable
//clean up the sort expression if needed - the
sort descending
//menu item causes the double in some cases
//Please note: this SortExpression is local
variable
if
(SortExpression.ToLowerInvariant().EndsWith("desc desc"))
SortExpression = SortExpression.Substring(0, SortExpression.Length -
5);
//need to handle the actual sorting of the data
if (!string.IsNullOrEmpty(SortExpression))
{
if (!string.IsNullOrEmpty(CallBackGridID) &&
CallBackGridID.Contains(ApproveGridViewID) &&
FilterDataFields.Contains(SortExpression.Split(' ')[0].ToString()))
{
DataView view = new DataView(dt);
view.Sort = SortExpression; //Local variable passed as parameter
DataTable newTable = view.ToTable();
dt.Clear();
dt = newTable;
}
else
{
DataView view = new DataView(dt);
view.Sort = SortExpressionApprove; //the property
DataTable newTable = view.ToTable();
dt.Clear();
dt = newTable;
}
}
#endregion
return dt;
}
private void setFilterFiledNameAttribute(string filterExpressionProperty, string[] filterSeperator, SPGridView objSPGrid)
{
//Set Filter Field Name
if (!string.IsNullOrEmpty(filterExpressionProperty))
{
string[] filterFields =
filterExpressionProperty.Split(filterSeperator, StringSplitOptions.RemoveEmptyEntries);
List<string> filterFiledNamesString = new List<string>();
foreach (string filterField in filterFields)
{
if (filterField.Split('=')[0] != null)
{
string tmpStr = filterField.Split('=')[0].ToString();
if (tmpStr.Contains("(")) tmpStr = tmpStr.Replace("(", "");
if (tmpStr.Contains(")")) tmpStr = tmpStr.Replace(")", "");
if (tmpStr.Contains("((")) tmpStr = tmpStr.Replace("((", "");
if (tmpStr.Contains("))")) tmpStr = tmpStr.Replace("))", "");
tmpStr = tmpStr.Trim();
if (!string.IsNullOrEmpty(tmpStr)) filterFiledNamesString.Add(tmpStr);
}
}
if (filterFiledNamesString.Count > 0)
objSPGrid.Attributes["FilterFieldName"] = string.Join(";",
filterFiledNamesString.ToArray());
}
}
protected override void OnPreRender(EventArgs e)
{
ScriptLink.Register(this.Page, "/_layouts/Multicolumnfilter-spgridview1/Custom_spgridview.js", false);
if
(!Page.IsPostBack)
gvDemo.DataBind();
//make sure the header images are set => Keep
it here !important
buildFilterView(gridds.FilterExpression);
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter writer)
{
gvDemo.Attributes["callbackEventReference"] = gvDemo.Attributes["callbackEventReference"].Replace("SPGridView_FilterCallbackHandler", "Custom_SPGridView_FilterCallbackHandler");
#region Microsoft Fix
try
{
if (gvDemo.HeaderRow != null)
{
if
(gvDemo.HeaderRow.Controls[0].Controls.Count > 0 &&
gvDemo.HeaderRow.Controls[0].Controls[0].GetType().Equals(typeof(Microsoft.SharePoint.WebControls.Menu)))
{
Microsoft.SharePoint.WebControls.Menu menu =
(Microsoft.SharePoint.WebControls.Menu)gvDemo.HeaderRow.Controls[0].Controls[0];
menu.ClientOnClickPreMenuOpen =
"Custom_SPGridView_FilterPreMenuOpen('" +
gvDemo.ClientID + "','" + gvDemo.ClientID +
"_SPGridViewFilterMenuTemplate',
'" + menu.ClientID + "', 'Title', event);" +
" MMU_Open(byid('" + gvDemo.ClientID + "_SPGridViewFilterMenuTemplate')," +
" MMU_GetMenuFromClientId('" + menu.ClientID + "'),event,true,null,0); return;";
//Custom_SPGridView_FilterPreMenuOpen(gridViewClientId,
templateClientId, menuClientId, dataFieldName, e)
}
if
(gvDemo.HeaderRow.Controls[1].Controls.Count > 0 &&
gvDemo.HeaderRow.Controls[1].Controls[0].GetType().Equals(typeof(Microsoft.SharePoint.WebControls.Menu)))
{
Microsoft.SharePoint.WebControls.Menu menu = (Microsoft.SharePoint.WebControls.Menu)gvDemo.HeaderRow.Controls[1].Controls[0];
menu.ClientOnClickPreMenuOpen =
"Custom_SPGridView_FilterPreMenuOpen('" +
gvDemo.ClientID +
"','" + gvDemo.ClientID +
"_SPGridViewFilterMenuTemplate',
'" + menu.ClientID + "', 'Field1', event);" +
" MMU_Open(byid('" + gvDemo.ClientID + "_SPGridViewFilterMenuTemplate')," +
" MMU_GetMenuFromClientId('" + menu.ClientID + "'),event,true,null,0); return;";
}
if
(gvDemo.HeaderRow.Controls[2].Controls.Count > 0 &&
gvDemo.HeaderRow.Controls[2].Controls[0].GetType().Equals(typeof(Microsoft.SharePoint.WebControls.Menu)))
{
Microsoft.SharePoint.WebControls.Menu menu =
(Microsoft.SharePoint.WebControls.Menu)gvDemo.HeaderRow.Controls[2].Controls[0];
menu.ClientOnClickPreMenuOpen =
"Custom_SPGridView_FilterPreMenuOpen('" +
gvDemo.ClientID + "','" + gvDemo.ClientID +
"_SPGridViewFilterMenuTemplate',
'" + menu.ClientID + "', 'Field2', event);" +
"
MMU_Open(byid('" + gvDemo.ClientID
+ "_SPGridViewFilterMenuTemplate')," +
"
MMU_GetMenuFromClientId('" + menu.ClientID + "'),event,true,null,0);
return;";
}
if
(gvDemo.HeaderRow.Controls[3].Controls.Count > 0 &&
gvDemo.HeaderRow.Controls[3].Controls[0].GetType().Equals(typeof(Microsoft.SharePoint.WebControls.Menu)))
{
Microsoft.SharePoint.WebControls.Menu menu =
(Microsoft.SharePoint.WebControls.Menu)gvDemo.HeaderRow.Controls[3].Controls[0];
menu.ClientOnClickPreMenuOpen =
"Custom_SPGridView_FilterPreMenuOpen('" +
gvDemo.ClientID + "','" + gvDemo.ClientID +
"_SPGridViewFilterMenuTemplate',
'" + menu.ClientID + "', 'Field3', event);" +
"
MMU_Open(byid('" + gvDemo.ClientID
+ "_SPGridViewFilterMenuTemplate')," +
"
MMU_GetMenuFromClientId('" + menu.ClientID + "'),event,true,null,0);
return;";
}
}
}
catch (Exception ex)
{
//To Do
}
#endregion
base.Render(writer);
}
|
Below Is the code for java script
that we included above
var KM_SPGridView_CallbackContext;
var enableCheckBox = false;
function
KM_SPGridView_FilterPreMenuOpen(gridViewClientId, templateClientId,
menuClientId, dataFieldName, e) { var gridView =
document.getElementById(gridViewClientId), callbackEventReference =
gridView.getAttribute("callbackEventReference"), callbackArgumentPrefix
= gridView.getAttribute("callbackArgumentPrefix"),
postbackEventReference =
gridView.getAttribute("postbackEventReference"), filterFieldName =
gridView.getAttribute("filterFieldName"), filterFieldValue =
gridView.getAttribute("filterFieldValue"), filterCurrentlyOn =
false; if (filterFieldName != null && filterFieldName.length > 0
&& filterFieldName.match(dataFieldName) != null) { filterCurrentlyOn
= true; enableCheckBox = true; } var menuTemplate =
document.getElementById(templateClientId), menuLink =
document.getElementById(menuClientId); if
(menuLink.getAttribute("suppressBubbleIfPostback") != null
&& e != null && e.srcElement != null &&
e.srcElement.href != null && e.srcElement.href.substr(0, MMU_postbackPrefix.length)
== MMU_postbackPrefix) { event.cancelBubble = true; return }
SPGridView_FixupFilterValuesFromMenuTemplate(menuTemplate,
filterCurrentlyOn); var menuItem = CAMOpt(menuTemplate,
Strings.STS.L_Loading_Text, "null");
menuItem.setAttribute("isFilterItem", "true");
menuItem.setAttribute("enabled", "false");
KM_SPGridView_CallbackContext = {};
KM_SPGridView_CallbackContext.gridViewClientId = gridViewClientId;
KM_SPGridView_CallbackContext.templateClientId = templateClientId;
KM_SPGridView_CallbackContext.menuClientId = menuClientId; KM_SPGridView_CallbackContext.dataFieldName
= dataFieldName; callbackEventReference =
callbackEventReference.replace(/__CALLBACKARGUMENT__/g, dataFieldName);
callbackEventReference =
callbackEventReference.replace(/__CALLBACKCONTEXT__/g, gridViewClientId + ";"
+ templateClientId + ";" + menuClientId + ";" +
dataFieldName); eval(callbackEventReference) }
function
KM_SPGridView_FilterCallbackHandler(q, p) { var a = p.split(";");
if (a.length != 4) { alert("ERROR: SPGridView_FilterCallbackHandler() -
values.length != 4"); return } var m = a[0], n = a[1], o = a[2], i =
a[3], c = document.getElementById(m); if (c == null) { alert("ERROR:
SPGridView_FilterCallbackHandler() - gridView == null"); return } var b
= document.getElementById(n); if (b == null) { alert("ERROR:
SPGridView_FilterCallbackHandler() - menuTemplate == null"); return }
var k = document.getElementById(o); if (k == null) { alert("ERROR:
SPGridView_FilterCallbackHandler() - menu == null"); return } var d =
c.getAttribute("postbackEventReference"); if (d == null || d.length
<= 0) { alert("ERROR: SPGridView_FilterCallbackHandler() -
postbackEventReference is null or empty"); return } var e =
c.getAttribute("filterFieldName"), l =
c.getAttribute("filterFieldValue"), h = false; if (e != null
&& e.length > 0 && e.match(i) != null) h = true;
SPGridView_FixupFilterValuesFromMenuTemplate(b, h); a =
q.split(";"); for (var f = 0; f < a.length; f++) { var g =
unescape(a[f]), r = d.replace(/__POSTBACKARGUMENT__/g, i + ";" +
g.replace(/\\/g, "\\\\").replace(/\'/g, "\\'").replace(/%/g,
"%25").replace(/;/g, "%3b")), j = CAMOpt(b, g, r);
j.setAttribute("isFilterItem", "true"); enableCheckBox
&& j.setAttribute("checked", "true");
enableCheckBox = false; } HideMenu(b); MMU_Open(b, k); popUpHandler() }
function popUpHandler() {
//Put "(Empty)" in popup Menu
var menuTxt = $('ul.ms-core-menu-list li:eq(5)').attr('text');
if ($.trim(menuTxt) == "") {
$('ul.ms-core-menu-list li:eq(5)
span.ms-core-menu-title').text("(Empty)");
}
}
|
Below is the output for above
example.
Thanks & Regards,
Keyur Pandya
Comments
Post a Comment