SharePoint: Adding a Toolbar to Custom List Views

You can download the source code for the web part and the required Feature from here:
Source Code: CustomListViewWebPart Article, deployment instructions at the end of the post.
Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.

Some time ago I wrote about using an EditorPart for a custom list view web part (read here) to allow users to easily configure the web part. Apart from the public and private responses I’ve received so far I made a promise that has yet to be fulfilled: adding a toolbar for the ListViewByQuery instance. In this post I will finally show how to do that.

Everyone just dropping in is advised to read my earlier post on the topic but may just as well stick to this one. Basically, the solution provided herein is an upgrade to the web part introduced previously. Either way, happy coding.

The Result

Below find a screen shot of my CustomListViewWebPart with a toolbar. The toolbar is fully functional and shows all the standard items users know from other lists.

CustomListViewWebPart with a standard toolbar

CustomListViewWebPart with a standard toolbar

Additionally, I am going to illustrate how to use templates for specific scenarios like displaying a single menu for printing as shown below.

CustomListViewWebPart with a custom toolbar

CustomListViewWebPart with a custom toolbar

OK, let’s jump right in.

The Strategy

To associate a toolbar (SharePoint coins it ViewToolBar) with our ListViewByQuery instance we have to pull some strings. Here is a preliminary list of points to consider:

  • All toolbar controls are defined in DefaultTemplates.ascx. We will define our own templates in a similar manner without touching the file.
  • Each List and library type can have its own toolbar template. The toolbar for a document library is slightly different from a document library. My implementation tries to cope with that.
  • We can easily create toolbars populated with our own menus and menu items. I will describe this process as well.

During the post I am going to refer to one or more files contained in the Visual Studio solution which is outlined below. That will help you to follow more easily.

Solution Structure for the CustomListViewWebPart

Solution Structure for the CustomListViewWebPart

One project called Deployment contains all files needed for deployment. It’s a WSPBuilder project, that allows creation and deployment of SharePoint solutions with a single click, among other useful stuff. You can copy the whole thing into the 12 hive, put the assembly in the GAC and activate the web part feature. That’s all it takes to get it running. For the impatient I’ve compiled some instructions at the end of this post.

The other project called SG.Global.SharePoint hosts the actual code. It is configured to emit an assembly (SG.Global.SharePoint.dll) that is being built in the GAC directory of the Deployment project. In case you’re wondering why: WSPBuilder uses a special directory structure for solution compilation, thus putting all assemblies for the solution into the GAC directory makes them part of the SharePoint solution.

Creating the Toolbar Templates

Interestingly, every .ascx file in the ControlTemplates of the 12 hive is parsed during to application startup which means that whatever control you put in there will be available as part of the object model. Pretty cool.

For our endavour, it means we create our own control file, place the templates in it and use it in our codes. My example implementation makes use of multiple templates since we need different toolbars for different types of lists and libraries.

Using the approach described above we will find six toolbar templates in the file SG.Global.SharePoint.WebControls.ascx source package:

  • SGBasicToolBar — Toolbar for custom implementations, references a CustomActionsMenu instance described later in this article.
  • SGBasicToolBarWithNew — Similar as the first one but contains a New button by default.
  • SGBasicToolBarWithNewUpload — This one is the same as the previous one but contains a custom Upload menu (which is useful if you want to disable the multiple file upload).
  • SGPictureLibraryViewToolBar — Default template for the picture library toolbar. Ready for tweaking.
  • SGDocumentLibraryViewToolBar — Default template for the document library toolbar. Actually not needed but can be customized right away.
  • SGViewToolBar — Default toolbar for libraries but without an upload menu.

As we can see our options are virtually unlimited once we’ve figured how to get things working.

Anatomy of a Toolbar Template

Let’s start with a standard toolbar. I’ve shamelessly copied the appropriate templates and directives from DefaultTemplates.ascx and modified them as needed. Don’t forget to Register a tag prefix for the provided or your own assembly using the Register directive:

<%@ Register TagPrefix="SG" Assembly="SG.Global.SharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=796c0d8ad6658654" namespace="SG.Global.SharePoint.WebControls"%>

Looking at the SGDocumentLibraryViewToolBar we see code like this:

<SharePoint:RenderingTemplate ID="SGDocumentLibraryViewToolBar" runat="server">
    <Template>
        <wssuc:ToolBar CssClass="ms-menutoolbar" EnableViewState="false" id="toolBarTbl" ButtonSeparator="<img src='/_layouts/images/blank.gif' alt=''>" RightButtonSeparator="&nbsp;&nbsp;" runat="server">
            <Template_Buttons>
                <SharePoint:NewMenu ID="SGNewMenu6" AccessKey="<%$Resources:wss,tb_NewMenu_AK%>" runat="server"/>
                <SharePoint:UploadMenu ID="SGUploadMenu6" AccessKey="<%$Resources:wss,tb_UploadMenu_AK%>" runat="server"/>
                <SharePoint:ActionsMenu ID="SGActionsMenu6" AccessKey="<%$Resources:wss,tb_ActionsMenu_AK%>" runat="server"/>
                <SharePoint:SettingsMenu ID="SGSettingsMenu6" AccessKey="<%$Resources:wss,tb_SettingsMenu_AK%>" runat="server"/>
            </Template_Buttons>
            <Template_RightButtons>
                  <SharePoint:PagingButton ID="SGPagingButton6" runat="server"/>
            </Template_RightButtons>
        </wssuc:ToolBar>
    </Template>
</SharePoint:RenderingTemplate>

It is vital for this solution to work that each toolbar template has a globally unique ID. It is therefore advisable to prefix each template to avoid collisions or unintended hiding of controls.

Within the Template_Buttons tags of the wssuc:ToolBar container we will find several menu items that provide the functions they are named with. Again I have prefixed each control ID to make them globally unique. I know that it may not be necessary in this case but I have experienced undesired side effects with non-unique IDs. YMMV.

Commenting out one or more elements causes the menus to disappear from the toolbar. Furthermore, an instance for each menu can be retrieved utilizing the object model, a fact that comes in handy if you need to perform conditional rendering.

Following this pattern I have created some templates and tweaked them accordingly. An empty toolbar similar to SGBasicToolBar would therefore look like this:

<SharePoint:RenderingTemplate ID="SGEmtpyToolBar" runat="server">
    <Template>
        <wssuc:ToolBar CssClass="ms-menutoolbar" EnableViewState="false" id="toolBarTbl" ButtonSeparator="<img src='/_layouts/images/blank.gif' alt=''>" RightButtonSeparator="&nbsp;&nbsp;" runat="server">
            <Template_Buttons></Template_Buttons>   
        </wssuc:ToolBar>
    </Template>
</SharePoint:RenderingTemplate>

Inspecting the file SG.Global.SharePoint.WebControls.ascx you will notice another RenderingTemplate with an ID of SGToolBarCustomActionsMenu. It serves as an example of how to use a FeatureMenuTemplate to add a new MenuItem to an existing menu. More on that later.

The Coding: Modifying CustomListViewWebPart

As outlined earlier I am not going over implementation details of the web part itself, but will explain the necessary changes to the code. If in doubt refer to CustomListViewWebPart.cs.

New Properties

In order to allow the user to change between the different toolbar styles I have implemented some check boxes that could easily be implemented as an enumeration but it gives you an idea:

/// 
/// Specifies whether a full tool bar shall be displayed
/// 
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Show ToolBar"),
 WebDescription("Show ToolBar")]
public bool ShowToolBar { get; set; }
 
/// 
/// Specifies whether a basic tool bar shall be displayed.
/// ShowToolbar must be true as well.
/// 
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Basic Toolbar Only"), WebDescription("Display basic toolbar, useful for lists from other sites.")]
public bool BasicToolBar { get; set; }
 
/// 
/// Loads a custom menu item allowing lists to be printed.
/// 
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Display 'Print List' button"),
 WebDescription("Indicates whether the Print List tool bar button should be displayed")]
public bool DisplayPrintListButton { get; set; }
 
/// 
/// Display 'Add new Item' button
/// 
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Display 'Add new Item' button"), WebDescription("Display 'Add new Item' button")]
public bool DisplayAddItemButton { get; set; }
 
/// 
/// Enables or disables filtering
/// 
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Disable Filter"), WebDescription("Disable Filter")]
public bool DisableFilter { get; set; }
 
/// 
/// Enables or disables sorting
/// 
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true), WebDisplayName("Disable Sort"), WebDescription("Disable Sort")]
public bool DisableSort { get; set; }

The checkboxes are part of the default EditorPart and not the CustomListViewEditorPart in order to keep things simple.

New Methods

First part of the magic is a the EnsureToolBar method that does some basic initialization and instantiates the correct template from our custom .ascx file.

/// 
/// Makes sure that a suitable toolbar is created for the
/// current view.
/// 
/// True if a toolbar has been created, false otherwise.
private bool EnsureToolBar() {
    if (toolBar != null)
        return true;
 
    if (SelectedView == null)
        return false;
 
    toolBar = new ViewToolBar(); // toolBar is a protected field
 
    if (BasicToolBar) {
        if (DisplayAddItemButton) {
            toolBar.TemplateName = "SGBasicToolBarWithNewUpload";
            var uploadMenu = new UploadMenu();
            uploadMenu.GetMenuItem("MultipleUpload").Visible = false;
        } else {
            toolBar.TemplateName = "SGBasicToolBar";
        }
    } else {
        toolBar.TemplateName = GetToolBarTemplateName(SelectedList);
    }
 
    toolBar.RenderContext = SPContext.GetContext(Context, SelectedView.ID, SelectedList.ID, SelectedWeb);
 
    if (!String.IsNullOrEmpty(SelectedView.ToolbarTemplateName)) {
        toolBar.TemplateName = SelectedView.ToolbarTemplateName;
    }
 
    return toolBar != null;
}

After creating a new instance of the ViewToolBar class it is important to obtain the correct SPContext for the list, view and site the ListViewByQuery is applied to. It serves as the RenderContext for the toolbar so that the menu items point to the correct URLs, for example.

What follows is highly implementation dependent but basically we associate a specific template with the toolbar instance we’ve just created. In my case I am following a specific logic to decide what toolbar to choose if the user has selected the Basic Toolbar and the Display New Item Button check boxes. In all other cases (if Basic Toolbar is not checked) I will dynamically choose the correct toolbar based on the type of list or library:

private static String GetToolBarTemplateName(SPList list) {
    switch (list.BaseTemplate) {
        case SPListTemplateType.DocumentLibrary:
        case SPListTemplateType.XMLForm:
            return "SGDocumentLibraryViewToolBar";
        case SPListTemplateType.AdminTasks:
        case SPListTemplateType.GenericList:
        case SPListTemplateType.IssueTracking:
        case SPListTemplateType.Tasks:
            return "SGViewToolBar";
        case SPListTemplateType.PictureLibrary:
            return "SGPictureLibraryViewToolBar";
        default:
            switch (list.BaseType) {
                case SPBaseType.DocumentLibrary:
                    return "SGDocumentLibraryViewToolBar";
                case SPBaseType.GenericList:
                case SPBaseType.Issue:
                    return "SGViewToolBar";
                default:
                    return String.Empty;
            }
    }
}

Modifying Some Old Methods

To make the new toolbar to appear we have to add some lines to CreateChildControls and RenderControl:

CreateChildControls

if (ShowToolBar &amp;&amp; EnsureToolBar())
    Controls.Add(toolBar);

RenderControl

toolBar.RenderControl(writer);

That’s all it takes to add a default toolbar to a ListViewByQuery. Neat, isn’t it?

Custom Toolbar Menus and Actions

The second screenshot above shows an example on how to create a toolbar from scratch for your very own solutions. We are now taking a closer look to the nuts and bolts of custom menus and actions.

Implementing a Custom Toolbar Menu

Let’s consider we are required to disable all toolbar buttons and instead allow users only to open the list in Excel and to print it.

Naturally, we would start with an empty toolbar template and add the menus and buttons as needed. Luckily, the SGBasicToolBar template is just what we need:

<SharePoint:RenderingTemplate ID="SGBasicToolBar" runat="server">
    <Template>
        <wssuc:ToolBar CssClass="ms-menutoolbar" EnableViewState="false" id="toolBarTbl" ButtonSeparator="<img src='/_layouts/images/blank.gif' alt=''>" RightButtonSeparator="&nbsp;&nbsp;" runat="server">
            <Template_Buttons>
                <SG:CustomActionsMenu ID="SGCustomActionsMenu1" runat="server" />
            </Template_Buttons> 
        </wssuc:ToolBar>
    </Template>
</SharePoint:RenderingTemplate>

The most important aspect of this template is the CustomActionsMenu element which is a custom implementation of a toolbar menu control derived from ToolBarMenuButton. The implementation can be found in CustomToolBarMenu.cs of the SG.Global.SharePoint.WebControls namespace. Let’s take a look:

[SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true),
 AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
 SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true),
 AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class CustomActionsMenu : ToolBarMenuButton {
    private Guid CurrentListId { get { return ListId; } }
    private Guid CurrentViewId { get { return base.List.Views[0] != null ? base.List.Views[0].ID : Guid.Empty; } }
    private string ListWebUrl { get { return base.List != null ? base.List.ParentWeb.Url : String.Empty; } }
 
    /// The default template name for this control.
    protected override string DefaultTemplateName { get { return "CustomActionsMenu"; } }
 
    /// The menu item's title text
    public override string Text { get { return "Actions"; } }
 
    protected override void AddMenuItems() {
        MenuTemplateControl.LargeIconMode = true;
 
        AddMenuItem(
            "1",
            "Export to Spreadsheet",
            "/_layouts/images/MenuSpreadsheet.GIF",
            "Analyse Items with a spreadsheet application",
            GetExcelRPCUrl(),
            "");
        AddMenuItem(
            "2",
            "Print",
            "/_layouts/images/LTTHGBRG.GIF",
            "Prints this list's current view",
            String.Format(
                "{0}/_layouts/PrintList.aspx?listWebUrl={0}&amp;listId={1}&amp;listViewId={2}",
                ListWebUrl,
                SPEncode.UrlEncode(CurrentListId.ToString()),
                SPEncode.UrlEncode(base.List.DefaultView.ID.ToString())
                ),
            "");
        //AddMenuItem("2", "New Itme 2", "", "Click here for next item ", "/_layouts/custompage.aspx", "");
    }
 
    private string GetExcelRPCUrl() {
        /*
         * RPC call to make a list available in Excel.
         * For more information see: 
         */
        const string formatString = "{0}/_vti_bin/owssvr.dll?CS=65001&amp;Using=_layouts/query.iqy&amp;List=%7b{1}%7d&amp;View=%7b{2}%7d&amp;CacheControl=1";
        string listGuid = System.Web.HttpUtility.UrlEncode(CurrentListId.ToString());
        string viewGuid = System.Web.HttpUtility.UrlEncode(CurrentViewId.ToString());
        string url = String.Empty;
 
        if (!String.IsNullOrEmpty(listGuid) &amp;&amp; !String.IsNullOrEmpty(viewGuid)) {
            url = String.Format(formatString, ListWebUrl, listGuid, viewGuid);
        }
 
        return url;
    }
}

First we override some properties to give the child a name (Text property) and a default template name which can be any name you desire but I recommend using nothing fancy like the class name implementing the control.

Second we override the AddMenuItems method that is documented extremely bad. Thanks to Reflector I got it working. The first call to AddMenuItem adds the Export to Spreadsheet requirement. The other one allows for printing of SharePoint lists (see the /_layouts/PrintList.aspx file for details).

Done. That was easy! Since we have referenced the control by name in our .ascx file it is automatically added to the toolbar at runtime.

In the same source file I am illustrating how to implement an Upload menu using the same approach. See CustomToolBarMenu.cs for details.

Adding an Action to an Existing Toolbar Menu

Suppose we want to add the Print List functionality to a single toolbar or all toolbars. The first requirement can be realized using the approach we’ve seen previously.

Open the file SG.Global.SharePoint.WebControls.ascx and take a close look at the RenderingTemplate with ID SGToolBarCustomActionsMenu. Instead of utilizing controls from ToolBar.ascx or ToolBarButton.ascx we use a FeatureMenuTemplate to add the Buttons to the Toolbar.

To enable printing for all lists I have included the PrintList.xml element file along with the SG.Global.SharePoint feature. The implementation can be seen in PrintMenuItemTemplate.cs in which a new MenuItemTemplate instance is attached to the current Control collection thus making it a child of the toolbar Actions menu item. Configuration is done in PrintList.xml.

Using the Visual Studio Solution

You can download the source code for the web part and the required Feature from here:
Source Code: CustomListViewWebPart Article
Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.

Since it is a Visual Studio 2008 Solution you should at least get your hands on VS 2008 Express which is available for free from Microsoft. Beyond that just follow this steps.

  • Fire up Visual Studio and compile the solution.
  • Copy the assembly SG.Global.SharePoint (or whatever you have renamed it to) to the GAC. By default the project is configured to emit the assembly to the GAC directory of the Deployment project.
  • Copy the complete TEMPLATE folder to the 12 hive and activate the feature SG.Global.SharePoint:
  • stsadm -o installFeature -name SG.Global.SharePoint
    stsadm -o activateFeature -name SG.Global.SharePoint -url [SITE_URL]
  • Add the following two lines to the SafeControls section of SITE_URL‘s web.config:
    
    
  • Navigate to the site at SITE_URL and add the web part. Start experimenting with it.

If you have any questions or suggestions, feel free to add a comment. Thanks for reading.







4 Responses to “SharePoint: Adding a Toolbar to Custom List Views”

[...] have finally published the successor of this article explaining how to associate toolbars with ListViewByQuery [...]

Steve Graegert » Blog Archive » Using the EditorPart to Create a Custom ListView Web Part added these pithy words on Oct 10 09 at 07:06

[...] SharePoint: Adding a Toolbar to Custom List Views [...]

Links (10/11/2009) « Steve Pietrek – Everything SharePoint added these pithy words on Oct 12 09 at 01:14

[...] menu. He didn’t know how to do it yet, so he asked his good friend, Google, that led him to a helpful blog post. Tom was successful and happy to create the shiny new menu under an hour, then he started to code [...]

Codebender » Working with SharePoint Lists in LiquidSilver added these pithy words on Dec 30 09 at 07:25

This is greate, but I would like to see it work with Document Libraries and be able to drill into folders, currently the URL for the Folder just refreshes the page. Any ideas what it would take for this to work, clicking the folder then changes the query to display only files contained within the selected folder. Thanks

rcarr added these pithy words on Mar 19 10 at 16:08

Leave a Reply

Bad Behavior has blocked 1665 access attempts in the last 7 days.