Wesley Bakker

Interesting things I encounter doing my job...

Sponsors

News

Wesley Bakker
motion10
Rivium Quadrant 151
2909 LC Capelle aan den IJssel
Region of Rotterdam
The Netherlands
Phone: +31 10 2351035

(feel free to chat with me)

Add to Technorati Favorites

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 file="ListItemSelection.js" company="motion10">
//     Copyright (c) motion10. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
 
function CreateParentInputCheckBox(webPartId) {
    return $("<th nowrap scope='col' class='ms-vh2'></th>").append(
                $("<input type='checkbox' title='(de)select all items' />").attr("id", webPartId + "0")
                .click(function() {
                    var checked = $(this).attr("checked");
                    $("[id^=" + webPartId + "_]").attr("checked", checked);
                })
            );
}
 
function CreateChildInputCheckBox(webPartId, itemId) {
    return $("<td></td>").append(
                $("<input type='checkbox' />").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 file="SelectItemsAction.cs" company="motion10">
//     Copyright (c) motion10. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
 
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 {
        /// <summary>
        /// Initializes a new instance of the <see cref="DownloadViewAsZipAction"/> class.
        /// </summary>
        public SelectItemsAction()
            : base("Enable item selection", "/_layouts/images/motion10/ListItemSelection.gif") {
            base.Description = "Enable the selection of items.";
        }
 
        /// <summary>
        /// Raises an event after the control is loaded but prior to rendering.
        /// </summary>
        /// <param name="args">An <see cref="T:System.EventArgs"></see> object that contains the event data.</param>
        [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);
        }
 
        /// <summary>
        /// Sends the content of the control to the specified <see cref="T:System.Web.UI.HtmlTextWriter"></see> object, which writes the content that is rendered on the client.
        /// </summary>
        /// <param name="output">The HtmlTextWriter object that receives the server control content.</param>
        [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}&amp;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}&amp;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

In my next post I’ll describe the DownloadAsZip feature which will contain a complete WSP file and source again which includes the SelectItemsAction. That feature uses the selected items to create a zip file.

Cheers and have fun!

Wes

Posted: Mar 05 2009, 01:30 PM by webbes | with 33 comment(s)
Filed under: ,

Comments

Arash Aghajani said:

wow! great post!

Wesley, i have 2 data view webpart in one aspx page.

i want when users select one or more items from first list,after clicking a button,automatically items adding to second list! i now that i should using checkbox for first list like your describtion, but how can i implement button to add selected item from first list to second list??

Thanks,

Arash

# March 6, 2009 2:47 PM

webbes said:

@Arash: You could use javascript to create such an option. The listview create a conext menu object for selectable lists. the first lists variable is called ctx1, the second one ctx2. From this variables you can request the list, the view and the folder that's currently selected. So your button could use the "function GetSelectedItemsString(webPartId)" to get the id's and the ctx2 variable to get the list and folder to copy to and then simple change the window.location to something like this:

mycopitemshandler.ashx?srcList=yoursourcelist&amp;Items=GetSelectedItemsResult&amp;destList=ctx2listname&amp;destFolder=ctx2RootFolder

The handler than should get the querystring variables en actually perform the action

Cheers,

Wes

# March 8, 2009 8:55 AM

Links (3/8/2009) « Steve Pietrek - Everything SharePoint said:

Pingback from  Links (3/8/2009) &laquo; Steve Pietrek - Everything SharePoint

# March 8, 2009 7:02 PM

Wesley Bakker said:

This is part 2 in the series where we create a feature that enables the download of multiple SharePoint list items as a zip. This post demonstrates how to stream a zip file containing sharepoint files and folders to the browser.

# March 9, 2009 1:07 PM

Arash Aghajani said:

Thanks Wesley,

I will do that..if i have a problem i'll comment here..

regards,

Arash

# March 15, 2009 12:25 PM

Duncan Gallimore said:

Help - this is exactly what I'm looking for but I can't follow your deployment path - are there some stages missing? Do I create a new folder in Features for this or use an existing one? How do I deploy the SelectItemsAction.cs?

Many thanks,

Duncan

# March 30, 2009 9:21 AM

webbes said:

Duncan,

I use WSP builder to create my SharePoint projects. I consider it the best tool to do so.

You can find WSPBuilder on codeplex:

www.codeplex.com/wspbuilder

Cheers,

Wes

# March 31, 2009 4:14 AM

Guillaume said:

Thanks for this awesome script, really made my day !

I do have a question though : I need to get the selected items and store them into a session variable, so i need to do this in #net and if I did understood the script properly I can only access them in javascript and have them into a URL. Would you have any idea on how to do this ??

Thanks,

# April 22, 2009 6:54 AM

nikson said:

hi, do i need to create a listview webpart, and why do we need this webpart.

# April 23, 2009 2:33 AM

webbes said:

@Guillaume: You can use an hidden field or the querystring(like I did) to capture the selected Id's on the server site. One way or the other, you need to have a trigger to postback the data though to place it in session. Another problem you might run into is that session state is turned OFF by default for SharePoint.

@nikson: This codesample is not about a new web part. It's about an extension to the existing listview webpart which enables you to select multiple items from the list. What you do with those items is up to you then. I've created another CustomAction to download the selected items as a zip file and a CustomAction to delete multiple items at once. You can use these selected items for a lot of new custom actions actually. f.e. multi print, multi move, multi copy, multi send to records center, multi rename(usefull with pictures..), multi resize, etc. etc.

Cheers,

Wes

# April 23, 2009 3:37 AM

nikson said:

hi,

I do understand wht this codesample is doing but got confused with listview webpart(now am clear!!!!!!!).

The thing is I tried to deploy the feature, during installation and activation it does not give any error but when i click on the action menu the option is not there.

I have copied and pasted the whole code, copied the .js file in layouts folder and GACed the dll as well.

can you pls help!!!!!!!!

# April 23, 2009 5:28 AM

webbes said:

@nikson: This walktrough is not complete yet. You can only add the multiselect with the code samples above. You should read the rest of this 3 part blogpost over here. You can also download the complete motion10 Solution Package that contains these and +- 15 other features.

# April 24, 2009 4:03 AM

nikson said:

hi,

I am working on visual studio 2005, how do i enable jquery or rather how do i make tht jquery code work.

# April 24, 2009 4:45 AM

webbes said:

@nikson: Have a look over here: weblogs.asp.net/.../sharepoint-2007-and-jquery-1.aspx

Unfortunately I cannot help you anymore than this. SharePoint is somewhat complicated and I do recommend you to read a couple of books first before you start to develop for SharePoint.

# April 24, 2009 4:54 AM

nikson said:

thanks for the link webbes, thts wht i was missing(jquery deployment in the page).

# April 24, 2009 8:00 AM

Ron said:

Thank you for the great post. The checkboxes do not work when using a view with groupings. Do you have ideas on how to display check boxes on an expanded group?

Thank you,

# May 15, 2009 9:59 AM

webbes said:

@Ron: It indeed does not work on grouped list views. And that's actually because the grouped items are only retrieved on expanding. SO the code needs to be adjusted and hook in to the expand method.

Cheers,

Wes

# June 2, 2009 3:47 AM

Shiva said:

Hi Wesley,

Thank you for the great post. Here we are able to select multiple items and delete them,but  i need some thing similar to this after selecting multiple items send them as a mail.

Thank you,

Shiv

# June 4, 2009 2:20 AM

webbes said:

@Shiva: You are not the only one with this request I can tell you. It does come with some difficulties though. Zipping the contents can take some time. By disabling the buffering we stream the zip directly to the client which prevents timeouts and this works intuitive.

Now how about if you would like to email the zipped items as an attachment? To prevent for timeouts, the only way to do this is asynchronously. But how do you inform the status of the email being send to the end user? For example? How do you collect the adresses etc. etc.

Indeed this can all be overcome but it takes some good architecture and coding and thus, if we built this feature it will not be free.

Cheers,

Wes

# June 4, 2009 5:08 AM

Shiva said:

Hi Wes,

Thank you very much for u reply. I am almost all done with this feature. Now I am able to send the individual items as an attachment (not zipped items). As u said its bit difficult to email the zipped items as an attachment Have to still work on that.

Warm Regards,

Shiva

# June 5, 2009 4:03 AM

zoha said:

Can you explain more

I don't find the _layout folder.

Can you give the code for an approve event for exemple

# June 17, 2009 9:23 AM

webbes said:

@Zoah

If you do not know about the _layouts folder you should start reading some books about SharePoint development first. The code represented on this page is just a startup on how you would go around in creating a solution like this. It's not a complete downloadable, installable sample. It requires some understanding of SharePoint programming. The best place to start SharePoint development is by attending a course or read some books. It is to complicated to fetch in a few blog posts or comments.

# June 17, 2009 10:04 AM

james said:

Webbes, you are my life saver. I am looking this for last 3 days.searching searching ......

Finally I got your blog. Thank you webbes.

Is there anyway without clicking 'Enable Item Selection'. User need direct on library. Deploy feature boom checkboxes appear then he will select and download.

Let me know how to do it?.

# July 22, 2009 10:58 AM

Srikanth Vadlakonda said:

Hi Webbes,

How are you? I need a small favor.

I need to work on same thing. But User doesn't want to click on menu item. By default checkboxes should load without any action. Please tell me how to approach this method.

# July 26, 2009 10:43 AM

webbes said:

@ Srikant and James:

You could add a delegate control to the AdditionalPageHeader which checks to see if it's on a listview page and then renders the necessary script.

You could also add a startupscript to the page which simply checks if there are listview web parts on the page.

And you could also use the same code as is right now and change startupscript that gets registered to include a call to 'ListItemSelection_ButtonClick(senderId, webPartId)' this does enable the clients to turn multiple selects off.

Cheers,

Wes

# July 27, 2009 3:25 AM

Srikanth said:

Please some code please. I am struggling.

# July 27, 2009 11:13 AM

webbes said:

@Srikanth: Sorry, I do not have the time to create custom code for each and everyone who needs a custom application.

# July 27, 2009 11:36 AM

Microsoft.Sharepoint said:

This is extremely useful. Makes a sharepoint list as flexible as a .net grid. Amazing stuff! thanks for sharing :)

# August 4, 2009 3:39 PM

James said:

Webbes,

Thanks for post. It is more helpful. Do you know the same printing selected documents? If you have any idea, Share with us.

# August 5, 2009 12:20 PM

avikumar said:

Hello Wesely

Is there any way to download the code or solution?

Avi

# September 2, 2009 11:39 AM

webbes said:

# September 3, 2009 8:41 AM

George said:

Hi Wesely,

I download your package and install it on a test MOSS 2007 server. The multi-select works fine when the site is under Default.Master (Sharepoint out of box master page).

However, when I switch to the desired master page (in my case, horizon_lay2_green.master, which is Microsoft sample master page, see www.wssdemo.com/.../master%20horizon.aspx), it stops working. When I click the "Enable item selection", nothing happened.

Do you have any idea about this? I think it is caused by different html layout contained in those 2 master pages. But I'm struggling to get it working.

Regards,

George

# November 5, 2009 8:09 PM

webbes said:

@George:

Your horizon_lay2_green.master is probably lacking the AdditionalPageHead delegate control(have a look at the default masterpage you'll find it in there)

I, and a lot of developers with me, use that AdditionalPageHead control to add JavaScript to the page.

# November 6, 2009 5:49 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)