Saturday, July 18, 2009

Adding Drop-Down Menu Items to SPGridView Using SPMenuField Control

When displaying custom data through SPGridView you won’t get the same look and feel of the default SharePoint List View. SPMenuField provides a data bound drop-down menu to be used in place of BoundField, TemplateField, or similar controls.

The following describe steps required to add drop-down menu items to a SPGridView control in Web Part.

  • Create a Web Part
    • Add project references: Microsoft.SharePoint and System.Web
    • Add the following namespaces to your class:

using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Collections.Generic;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

    • Inherits your class with Microsoft.SharePoint.WebPartPages.WebPart

public class SampleGrid : WebPart
{
}

  • Override CreateChildControls method, create the ObjectDataSource, SPGridView, SPGridViewPager and SPMenuField controls as shown below:

protected override void CreateChildControls()
{
    // Why UseDefaultStyles = false?
    // See http://sharepointmalaya.blogspot.com/2009/07/how-to-make-spgridview-to-have-same.html for details
    this.UseDefaultStyles = false;

    // ObjectDataSource
    ObjectDataSource dataSource = new ObjectDataSource();
    dataSource.ID = "dataSource";
    dataSource.SelectMethod = "SelectData";
    dataSource.TypeName = this.GetType().AssemblyQualifiedName;
    dataSource.ObjectCreating += new ObjectDataSourceObjectEventHandler(objectCreating);
    this.Controls.Add(dataSource);

    // SPGridView
    spGridView = new SPGridView();
    spGridView.ID = "spGridView";
    spGridView.DataSourceID = dataSource.ID;
    spGridView.AutoGenerateColumns = false;
    spGridView.AllowPaging = true;
    spGridView.PageSize = 10;
    spGridView.AllowSorting = true;
    spGridView.EmptyDataText = string.Empty;
    this.Controls.Add(spGridView);

    // SPGridViewPager
    pager = new SPGridViewPager();
    pager.GridViewId = spGridView.ID;
    this.Controls.Add(pager);

    // SPMenuField - Name field
    SPMenuField nameCol1 = new SPMenuField();
    nameCol1.HeaderText = "Name";
    nameCol1.TextFields = "Name";
    nameCol1.NavigateUrlFields = "Name,Email,WebSite";
    nameCol1.NavigateUrlFormat = "/_layouts/sharepointmalaya/DispDetails.aspx?WebPartID=" + this.ID + "&FilterName={0}&ReturnUrl=" + Page.Request.Url.ToString();
    nameCol1.TokenNameAndValueFields = "KEYNAME=Name,KEYEMAIL=Email,KEYWEBSITE=WebSite";
    nameCol1.SortExpression = "Name";
    nameCol1.MenuTemplateId = "menuTemplate";

    // MenuTemplate
    MenuTemplate menuTemplate = new MenuTemplate();
    menuTemplate.ID = "menuTemplate";
    this.Controls.Add(menuTemplate);
    spGridView.Columns.Add(nameCol1);

    // MenuTemplate - View Item
    MenuItemTemplate menuItemTemplate0 = new MenuItemTemplate("View Item", "~/_layouts/images/LIST.GIF");
    menuItemTemplate0.ClientOnClickNavigateUrl = "/_layouts/sharepointmalaya/DispDetails.aspx?WebPartID=" + this.ID + "&FilterName={%KEYNAME%}&ReturnUrl=" + Page.Request.Url.ToString();
    menuTemplate.Controls.Add(menuItemTemplate0);

    // MenuTemplate - Seperator
    MenuSeparatorTemplate menuSepTemplate = new MenuSeparatorTemplate();
    menuTemplate.Controls.Add(menuSepTemplate);

    // MenuTemplate - Open WebSite
    MenuItemTemplate menuItemTemplate1 = new MenuItemTemplate("Open WebSite", "~/_layouts/images/ASP16.GIF");
    menuItemTemplate1.ClientOnClickNavigateUrl = "javascript:window.open('%KEYWEBSITE%');";
    menuTemplate.Controls.Add(menuItemTemplate1);

    // MenuTemplate - Send Email
    MenuItemTemplate menuItemTemplate2 = new MenuItemTemplate("Send Email", "~/_layouts/images/EML16.GIF");
    menuItemTemplate2.ClientOnClickScript = "javascript:document.location.href='MailTo:%KEYEMAIL%;'";
    menuTemplate.Controls.Add(menuItemTemplate2);

    // BoundField - Email field
    BoundField nameCol2 = new BoundField();
    nameCol2.DataField = "Email";
    nameCol2.SortExpression = "Email";
    nameCol2.HeaderText = "Email";
    spGridView.Columns.Add(nameCol2);

    // BoundField - WebSite field
    BoundField nameCol3 = new BoundField();
    nameCol3.DataField = "WebSite";
    nameCol3.SortExpression = "WebSite";
    nameCol3.HeaderText = "WebSite";
    spGridView.Columns.Add(nameCol3);

    this.ChildControlsCreated = true;
}

  • Code above create three (3) drop-down menu items and a separator menu as shown below:

image

  • Clicking on the “View Item” redirects to custom page in the _layouts folder. When clicking on the “Open WebSite”, it opens web site and “Send Email” menu open client email form.
  • Please note SPMenuField has a property TokenNameAndValueFields which can be used to store token names and values in the following format:

SPMenuField.TokenNameAndValueFields = "KEYNAME=Name,KEYEMAIL=Email,KEYWEBSITE=WebSite";

  • To consume SPMenuField token values, you must specify token name within the % sign as shown below:

“%TOKEN_NAME%” OR “%KEYWEBSITE%”

  • As shown in code example above, ObjectDataSource control require “SelectData” method. You need to create a public method SelectData which returns a System.Data.DataTable – a dummy data which has three (3) columns which are “Name”, “Email” and “WebSite”. See below:

public DataTable SelectData()
{
    DataTable table = new DataTable();
    table.Columns.Add("Name");
    table.Columns.Add("Email");
    table.Columns.Add("WebSite");

    table.Rows.Add("SharePoint Malaya","sharepointmalaya@gmail.com","http://sharepointmalaya.blogspot.com");
    table.Rows.Add("Faris","faris@gmail.com","http://sharepointmalaya.blogspot.com");
    table.Rows.Add("Farhana","farhana@gmail.com","http://sharepointmalaya.blogspot.com");

    return table;
}

  • In addition, ObjectDataSource control implement event handler ObjectCreating – You also need to create instance containing the event data as shown below:

private void objectCreating(object sender, ObjectDataSourceEventArgs e)
{
    e.ObjectInstance = this;
}

  • Next, bind the data with the SPGridView control as shown below:

protected override void Render(HtmlTextWriter writer)
{
    if (ChildControlsCreated)
        spGridView.DataBind();
    base.Render(writer);
}

  • Build and deploy to SharePoint site. That’s it – All Done. See below for screenshot:

image

Get sample code here:

2 comments:

sukri said...

Nice - I like it (Y)

Anonymous said...

I'm following this excellent article on how to take advantage of these classes. Got working most of what I need, but hit a snag.

I'm adding menus to my items. The tokens are resolving OK, except that in one of my urls I have a repeated token. Like this:

targetColumn.TokenNameAndValueFields = "HOSTURL=HostUrl,DOCID=DocumentId,LISTID=ListId";

ModerateMenuItem.ClientOnClickNavigateUrl= %HOSTURL%/_layouts/... (a bunch of stuff) ... &ListId={%LISTID%}&Source=%HOSTURL%&IsDlg=1

The first %HOSTURL% is matched to the property on the data source, but the second is not. It renders as %HOSTURL%

I tried adding a second HOSTURL entry on TokenNameAndValueFields I get an exception:

System.ArgumentException - mscorlib

Message: An item with the same key has already been added.

Tried with another token name, say HOSTURL2=HostUrl. Same exception

Any ideas?