Kirk Evans Blog

.NET From a Markup Perspective

The Content Query Web Part, and Working With Lookup Fields in a Custom Web Part

I am helping work on a proof of concept for a customer, and one of the requirements is to display data from a list multiple times throughout the site.  Sweet, I can do that, the Content Query Web Part fits the bill nicely here. 

The Content Query Web Part is an extremely useful web part that allows you to reuse content in multiple locations in your site without duplicating the data in multiple places.  The simplest way to show this is, well, to just show you!  Given the following list:

image

I can surface that data on other pages pretty easily without requiring developer intervention.

image

 

The image to the left shows the settings for the Content Query Web Part.  I can point it to any item on this site, to items on a specific site, or to a specific list, all through a simple radio button list.

I can also configure a filter.  This allows you to choose from various field types and set conditions based on the field.  Here, I look for the Title column type and only show values when that field’s contents are equal to “First item”.  The result is that the web part displays the first item.

image

There are some fantastic resources at the end of this blog on CQWP and a CodePlex project called Enhanced Content Query Web Part, or ECQWP, that I highly recommend you take the time to review.  This is a very powerful feature of SharePoint that you may not have realized is there.

I was all set to use CQWP for my proof of concept when the next requirement surfaced… the source list will use a lookup field to another list, and we need to filter based on the lookup field value.  After several failed attempts, I found this entry on the MSDN Forums that says that the Cross List Query, which is the data source for CQWP, does not support choice or lookup fields that have multi-choice enabled.  Seems to explain why I couldn’t get to the data. 

Even though there are great tools like the Content Query Web Part, there are still times when you want to do something that the tools can’t do.  That’s when it’s time to dive into the API and see how easy it is to build functionality yourself.  Here is what I came up with.  This example demonstrates how to get to the values of a multi-choice lookup field, as well as showing how to add attributes to influence the property sheet for a web part.

using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Data;
using System.ComponentModel;

namespace DPE.Samples
{
    [Guid("346b1693-82b1-45ba-ab3b-8a1c35faa0e5")]
    public class POCWebPart : System.Web.UI.WebControls.WebParts.WebPart
    {
        public POCWebPart()
        {
        }

        [WebBrowsable]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Filter Properties")]
        [Description("The name of the list to pull data from")]
        [FriendlyName("List Name")]
        public string ListName { get; set; }

        [WebBrowsable]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Filter Properties")]
        [Description("The name of the field to display (default is 'Text')")]
        [FriendlyName("Display Field Name")]
        public string DisplayFieldName { get; set; }

        [WebBrowsable]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Filter Properties")]
        [Description("The name of the lookup field in the source list to filter values (default is 'Metadata')")]
        [FriendlyName("Lookup Field Name")]
        public string LookupFieldName { get; set; }

        [WebBrowsable]
        [Personalizable(PersonalizationScope.Shared)]
        [Category("Filter Properties")]
        [Description("The string literal value to filter data on")]
        [FriendlyName("Filter Value")]
        public string FilterValue { get; set; }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            if ((null != ListName))
            {
                SPList list = SPContext.Current.Web.Lists[ListName];

                
                DataTable dt = new DataTable();
                dt.Columns.Add("data");
                string lname = LookupFieldName == null ? "Metadata" : LookupFieldName;

                foreach (SPListItem item in list.Items)
                 {
                    
                       SPFieldLookupValueCollection values = new SPFieldLookupValueCollection(item[lname].ToString());
                    foreach (SPFieldLookupValue value in values)
                    {
                        if (value.LookupValue.Equals(this.FilterValue,StringComparison.InvariantCultureIgnoreCase))
                        {
                            dt.Rows.Add(DisplayFieldName == null ? item["Title"] : item[DisplayFieldName]);
                        }
                    }
                }
                dt.AcceptChanges();

                SPGridView grid = new SPGridView();
                grid.ID = "ExampleGrid";
                grid.AutoGenerateColumns = false;


                BoundField col = new BoundField();
                col.DataField = "data";
                col.SortExpression = "data";
                col.HeaderText = this.DisplayFieldName;
                grid.Columns.Add(col);

                grid.DataSource = dt;
                grid.DataBind();

                Controls.Add(grid);
            }
            
        }
    }
}

So, what does this actually do?  Start with a list that contains a column called “Metadata” that is a multi-choice lookup field.

clip_image002

Add the web part and setting the properties, setting the filter value to a string literal (from the image above, would be one of “foo”, “bar”, or “baz”).

clip_image002[4]

The web part will now display the specified column for rows meeting the filter condition.

clip_image002[6]

Another thing to point out here is that I am using the SPGridView instead of the ASP.NET GridView because the SPGridView has support for SharePoint’s cascading style sheets.  This makes it much easier for my web part to conform to the styles used throughout the site instead of managing this on my own.

For more information:

Configuring and Customizing the Content Query Web Part

Customizing the Content Query Web Part and Custom Item Styles

Enhanced Content Query Web Part