Saturday, February 5, 2011

Select Multiple List Items in SharePoint Feature

Default a SharePoint list does not have an option to select multiple items and looks like this:
List
Today I’m going to show you how to create a feature that enables the selection of multiple list items to make it look like this(notice the checkboxes):
SelectableList

JavaScript

I decided to go with some jQuery to adjust the list on the client side and one action button that simply adds or removes the checkboxes from the list. It’s all not to difficult if we use jQuery. So here’s the JavaScript:
//-----------------------------------------------------------------------
// 
//     Copyright (c) motion10. All rights reserved.
// 
//-----------------------------------------------------------------------
 
function CreateParentInputCheckBox(webPartId) {
return $("").append(
$("").attr("id", webPartId + "0")
.click(function() {
var checked = $(this).attr("checked");
$("[id^=" + webPartId + "_]").attr("checked", checked);
})
);
}
 
function CreateChildInputCheckBox(webPartId, itemId) {
return $("").append(
$("").attr("id", webPartId + "_" + itemId)
.val(itemId)
.click(function() {
$("#" + webPartId + "0").attr("checked", $(this).attr("checked") && $("[id^=" + webPartId + "_]:not(:checked)").length == 0);
})
);
}
 
function AddCheckBoxesToListView(webPartId) {
$("#" + webPartId + " table.ms-listviewtable>tbody")
.find(">tr.ms-viewheadertr").prepend(CreateParentInputCheckBox(webPartId)).end()
.find(">tr:not(.ms-viewheadertr)")
.each(function() {
var itemId = $(this).find("td.ms-vb-title>table[id]").attr("id");
if (itemId) {
$(this).prepend(CreateChildInputCheckBox(webPartId, itemId));
}
});
}
 
function IsSelectable(webPartId) {
var selectableItems = $("#" + webPartId + " table.ms-listviewtable>tbody>tr:not(.ms-viewheadertr)>td.ms-vb-title>table[id]").length;
return selectableItems > 0;
}
 
function RemoveCheckBoxesFromListView(webPartId) {
$("[id^=" + webPartId + "_], #" + webPartId + "0").parent().remove();
}
 
function GetSelectedItemsString(webPartId) {
var selectedIds = new Array();
$("[id^=" + webPartId + "_]:checked")
.each(function() {
selectedIds.push($(this).val());
});
 
return selectedIds.join(",");
}
 
function ListItemSelection_ButtonClick(senderId, webPartId) {
//jQueryon mozilla does not work with namespaces. We have to work with plain old javascript here...
var sender = document.getElementById(senderId);
 
if (sender.getAttribute("remove")) {
RemoveCheckBoxesFromListView(webPartId);
sender.setAttribute("text" ,"Enable item selection");
sender.setAttribute("description", "Enable the selection of items.");
sender.removeAttribute("remove");
} else {
AddCheckBoxesToListView(webPartId)
sender.setAttribute("text", "Disable item selection")
sender.setAttribute("description", "Disable the selection of items.");
sender.setAttribute("remove", true);
}
}
 
function ListItemSelection_Init(senderId, webPartId) {
if (!IsSelectable(webPartId)) {
var sender = document.getElementById(senderId);
sender.parentNode.removeChild(sender);
}
}
We create this ListItemSelection.js file and add it to our layouts directory. I use WSPBuilder for it, but you can use whatever you like to use for it.
The JavaScript checks if there’s a title column with edit control block in the listview because this field will contain the id of the item. If not, the button is removed because we can’t do anything without an id.

The button

“Which button?” you ask. We have to create a custom action button. We do need to have a custom action that registers the above mentioned JavaScript file and a startup script to verify we have a title column with edit control block. This isn’t that difficult at all. The code looks like this:
//-----------------------------------------------------------------------
// 
//     Copyright (c) motion10. All rights reserved.
// 
//-----------------------------------------------------------------------
 
using System.Globalization;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
 
namespace Motion10.SharePoint2007 {
public class SelectItemsAction : MenuItemTemplate {
/// 
/// Initializes a new instance of the  class.
/// 
public SelectItemsAction()
: base("Enable item selection", "/_layouts/images/motion10/ListItemSelection.gif") {
base.Description = "Enable the selection of items.";
}
 
/// 
/// Raises an event after the control is loaded but prior to rendering.
/// 
/// An  object that contains the event data.
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
protected override void OnPreRender(System.EventArgs args) {
base.OnPreRender(args);
if (this.ListViewWebPart == null) {
return;
}
 
if (!Page.ClientScript.IsClientScriptIncludeRegistered("ListItemSelection")) {
Page.ClientScript.RegisterClientScriptInclude("ListItemSelection", "/_layouts/ListItemSelection.js");
}
 
string startupScript = string.Format(CultureInfo.InvariantCulture,
"$(function(){{ListItemSelection_Init('{0}', 'WebPart{1}');}});",
this.ClientID,
this.ListViewWebPart.Qualifier);
 
Page.ClientScript.RegisterStartupScript(typeof(SelectItemsAction), this.ClientID, startupScript, true);
}
 
/// 
/// Sends the content of the control to the specified  object, which writes the content that is rendered on the client.
/// 
/// The HtmlTextWriter object that receives the server control content.
[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
protected override void Render(System.Web.UI.HtmlTextWriter output) {
if (this.ListViewWebPart == null || this.ListViewWebPart.ViewType != ViewType.Html) {
this.Visible = false;
}
 
if (this.Visible) {
string clientScript = string.Format(CultureInfo.InvariantCulture,
"ListItemSelection_ButtonClick('{0}', 'WebPart{1}')",
this.ClientID,
listViewWebPart.Qualifier);
 
this.ClientOnClickScript = clientScript;
}
 
base.Render(output);
}
 
private bool searchedForListView = false;
private ListViewWebPart listViewWebPart;
private ListViewWebPart ListViewWebPart {
get {
if (!searchedForListView) {
listViewWebPart = FindListView(this.Parent);
}
 
return listViewWebPart;
}
}
 
private static ListViewWebPart FindListView(Control parent) {
ListViewWebPart retVal = parent as ListViewWebPart;
if (retVal != null) {
return retVal;
}
 
if (parent.Parent == null) return null;
 
return FindListView(parent.Parent);
}
}
}
With this code we have a custom action class but it’s not bound to any list toolbar yet. That's what we'll do next.

The feature

In order to have this button available on lists we need to add a CustomAction to our features Elements.xml file. In my case I’m going to use the selected items to download them as a zip file. So I added this custom action to the "Download as Zip" feature. You can however use this custom action for any feature you can think of. Such as a DeleteMultipleItemsAtOnce feature. The elements.xml file looks like this:
xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Id="{DE394AD0-0A8E-4e5c-B246-A498BA2A7FB2}"
Title="Download as Zip"
RegistrationType="List"
RegistrationId="101"
Location="Microsoft.SharePoint.StandardMenu"
GroupId="ActionsMenu"
ControlAssembly="SharePointSolutionPack, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4a7cd02bdf107f7a"
ControlClass="Motion10.SharePoint2007.DownloadAsZipAction">
CustomAction>
<CustomAction
Id="{CB4BE13C-C095-4a02-B875-787325045759}"
Title="Enable item selection"
RegistrationType="List"
RegistrationId="101"
Location="Microsoft.SharePoint.StandardMenu"
GroupId="ActionsMenu"
ControlAssembly="SharePointSolutionPack, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4a7cd02bdf107f7a"
ControlClass="Motion10.SharePoint2007.SelectItemsAction">
CustomAction>
<CustomAction
Id="{18A0608F-7917-4fa3-8164-18E81B55A551}"
ImageUrl="/_layouts/images/ICZIP.GIF"
Title="Download as Zip"
Description="Download all files in this folder and view as one zip"
RegistrationType="ContentType"
RegistrationId="0x0120"
Location="EditControlBlock">
<UrlAction Url="{SiteUrl}/_layouts/DownloadAsZip.ashx?List={ListId}&Item={ItemId}" />
CustomAction>
<CustomAction
Id="{175D475D-C962-4965-9C9B-7CAFBB36A669}"
ImageUrl="/_layouts/images/ICZIP.GIF"
Title="Download as Zip"
Description="Download this file as zip"
RegistrationType="ContentType"
RegistrationId="0x0101"
Location="EditControlBlock">
<UrlAction Url="{SiteUrl}/_layouts/DownloadAsZip.ashx?List={ListId}&Item={ItemId}" />
CustomAction>
Elements>
As you can see we’ve added this custom action as the second element in our DownloadAsZip features Elements.xml file. In this particular case we've tight this action to document library lists. That's because we do not have any other feautres yet that use the selected items.

Conclusion

Once deployed we have this extra button that toggles item selection:
EnableSelection
DisableSelection

No comments:

Post a Comment