Thursday, August 30, 2007

SPGridview and collapsed grouping by default!

This really bugged me when I came across the SPGridView (as you can read in the previous about this control) that when you set the following properties

oGrid.AllowGrouping = true;
oGrid.AllowGroupCollapse = true;

You'd expect that there is another property where you can specify if you want to have it collapsed or expanded when the Grid gets rendered. Unfortunately there isn't.. but (and that's why I'm posting ;)) there is dirty way to do it and that's injecting some javascript after the grid gets rendered. Thanks to my collegua Servé "CSS/HTML & Javascript Wizard" Hermans the following bit of script gets the job done.

<script type="text/javascript">
var rows = document.getElementsByTagName('tr');
var numRows = rows.length;
for (var i = 0; i < numRows; ++i) 
{
    if (rows[i].getAttribute("isexp") != null && rows[i].getAttribute("isexp").toLowerCase() == "true")
    {
          if(rows[i].firstChild.firstChild != null && rows[i].firstChild.firstChild.tagName.toLowerCase() == "a")
          {    
              if (rows[i].firstChild.firstChild.title.toLowerCase() == "expand/collapse")
                  {
                    rows[i].firstChild.firstChild.fireEvent("onclick");
            }
          }
    }

}
</script>

To explain it the script.. an event called "ToggleSPGridViewGroup" (which can be found in the spgridview.js in the Layouts folder) is fired when you click on the expand/collapse icon on the grouped item. So I thought "Why not fire all of the links to have it all collapsed by default?" :) I thought this wasn't possible since it seems REALLY dirty to actually fire events without user interaction. But it actually is possible! Thank god ;)

Technorati tags: ,

28 comments:

Anonymous said...

If you were to incorporate this script into a web part, would adding it via the ClientScriptManager.RegisterClientScriptBlock work?

I have attempted this and it doesn't seem to be working. I'm not getting any errors, but the groups are not collapsing either.

Does the register block in the OnLoad event allow the script to actually fire on the page?

Robin Meuré said...

Hi Randy,

well what I did was the include the script using the Render function. And I 'display' this bit of javascript at the very end. I will post some code example tomorrow!

Anonymous said...

I got it working now. What I did was to add the script block at the OnLoad event but I made it a function. I then called the function using a literal control added at the end of CreateChildControls.

Thanks for the work-around!

Anonymous said...

try this http://msdn2.microsoft.com/en-us/library/ms948916.aspx

sam from chicago

Anonymous said...

Hi Robin,

Could you please post a small example of, how you add the script and call it on your aspx-page?

Thank you

Lasse from Denmark

Anonymous said...

It works well putting into a Content Editor Web Part after the grid.

Sandro Sanchez said...

Thanks it works
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
//All Code
}
writer.Write(DisplayScript());
}

In DisplayScript is the java script

Adam Casey said...

Is there any way of doing this without a front web page? pure c#

Thank you

john bailo said...

For JQuery users, I was able to do this as the single line:

$('a[title^="Expand"]').trigger("click");

Thanks for the article!

john bailo said...
This comment has been removed by the author.
Robin Meuré said...

Hi John,

thanks.. and indeed very clever to use JQuery for stuff like this! :)

Anonymous said...

Thanks for the tip! I had to modify your script a bit after I found out that my Polish version of SharePoint rendered 'expand/collapse' titles as their Polish equivalents.

Albert said...

Lots of thanks for the code...it worked perfectly to me.

In my case, I used Sharepoint Designer, and I paste the javascript below this label:

ContentPlaceHolderId="PlaceHolderBodyAreaClass"

Anonymous said...

I found some problems when I used your JS code.I was obliged to use many firstchild properties and with sharepoint in french the expand/collapse is replaced by another title!!

eric said...

Thanks for the post! This is great stuff, only like Anonymous, I had many more nested children, so this didn't work for me.

Instead of using a bunch of "firstChild" references, I whittled the code down to just search for "a" tags with the appropriate title.

For what it's worth, here's what works best with my SPGridView:

var rows = document.getElementsByTagName('a');
var numRows = rows.length;

for (var i = 0; i < numRows; ++i)
{
if (rows[i].title.toLowerCase()=="expand/collapse")
{
rows[i].fireEvent("onclick");
}
}

Unknown said...

Eric, the only problem with your approach is that you're getting all elements with a tag of 'a', which, as we all know, can be highly unpredictable. Try using the original approach first.

PM said...

I have a question:

Does this code work if you need 2 levels of nested sub-grouping.

Example:

XYZ corp- Sales (first child) - US Sales (second child)

Hope you got what I am trying to say.

Sandeep Jha said...

Example: To call JS in web part.

protected override void RenderContents(HtmlTextWriter writer)
{
writer.Write(errorMessage);
writer.Write(DisplayScript());
EnsureChildControls();
RenderChildren(writer);
}

private string DisplayScript()
{
StringBuilder strCollapseScript = new StringBuilder();
strCollapseScript.Append("--script language='javascript'--");
strCollapseScript.Append("var rows = document.getElementsByTagName('tr');");
strCollapseScript.Append("var numRows = rows.length;");
strCollapseScript.Append("for (var i = 0; i < numRows; ++i) ");
strCollapseScript.Append("{");
strCollapseScript.Append("if (rows[i].getAttribute('isexp') != null && rows[i].getAttribute('isexp').toLowerCase() == 'true')");
strCollapseScript.Append("{");
strCollapseScript.Append("if(rows[i].firstChild.firstChild != null && rows[i].firstChild.firstChild.tagName.toLowerCase() == 'a')");
strCollapseScript.Append("{ ");
strCollapseScript.Append("if (rows[i].firstChild.firstChild.title.toLowerCase() == 'expand/collapse')");
strCollapseScript.Append("{");
strCollapseScript.Append("rows[i].firstChild.firstChild.fireEvent('onclick');");
strCollapseScript.Append("}");
strCollapseScript.Append("}");
strCollapseScript.Append("}");
strCollapseScript.Append("}");
strCollapseScript.AppendLine("--/script--");
Page.ClientScript.RegisterStartupScript(Page.GetType(), "SearchScript", strCollapseScript.ToString());
return string.Empty ;
}

replace -- with greater sign.

Anonymous said...

None of these options appear to be working for me. The grid loads but all of the groups are still expanded.

Murali said...

Use this:

if (rows[i].firstChild.firstChild.title.indexOf('Expand/Collapse')==0) {
rows[i].firstChild.firstChild.fireEvent("onclick");
}

chris badenhorst said...

Got this working as well, embedded my script as a function at the top of my sharepoint web part just after the @register and @assembly registrations. Then injected an ASP LiteralControl like the following:

protected override void CreateChildControls()
{
Control control = Page.LoadControl(_ascxPath);
Controls.Add(control);
Controls.Add(new LiteralControl("--script type='text/javascript'>CollapseAll()--/script--"));
}

Nuisance that CollapseAll is not provided

Anonymous said...

Please right indexOf("expand/collapse")

if (rows[i].firstChild.firstChild.title.toLowerCase().indexOf("expand/collapse") != -1) {
......
}

instade of comparing the value like

if (rows[i].firstChild.firstChild.title.toLowerCase() == "expand/collapse")
{
...........
}

Anonymous said...

instead of code

if (rows[i].firstChild.firstChild.title.toLowerCase() == "expand/collapse")
{
....
}

Please write

if (rows[i].firstChild.firstChild.title.toLowerCase().indexOf("expand/collapse") != -1) {
....
}

Anonymous said...

instead of code

if (rows[i].firstChild.firstChild.title.toLowerCase() == "expand/collapse")
{
....
}

Please write

if (rows[i].firstChild.firstChild.title.toLowerCase().indexOf("expand/collapse") != -1) {
....
}

Anonymous said...

instead of code

if (rows[i].firstChild.firstChild.title.toLowerCase() == "expand/collapse")
{
....
}

Please write

if (rows[i].firstChild.firstChild.title.toLowerCase().indexOf("expand/collapse") != -1) {
....
}

Unknown said...

If some one wants to make grouping collapse by default using Jquery please use the below code:



$(document).ready(function () {
$("#<%=YourGridViewId.ClientID %> tr[isexp$='true']").each(function () {
$(this).find('a').click();
});
});

Firdouz Hussain said...

Its not working in IE11 and chrome.Can you please specify where i have to change.

PappieFlappie said...

jQuery('tr[isexp="true"] a[title^="Expand/Collapse"]').click()