Friday, October 22, 2010

XsltListViewWebPart – how to hide the ribbon

A reader of my blog asked me this in a comment to one of the previous postings and I had also wondered about it myself on several occasions. The thing is that sometimes you may not want the ribbon to appear when you select (clicking on the web part basically selects it) the XLV web part or select some of the items that the web part displays. The solution to this one turned out to be pretty simple and straight-forward – it involves creating a small custom XSLT file which does the trick with several lines of javascript (the selection of the web part and the displaying/hiding of the ribbon are all implemented with javascript in standard SharePoint 2010, so it is pretty logical to counter this also with javascript). The replacing of the standard “main.xsl” in the XslLink property of the web part and the setting of one additional property of its hidden SPView are then the only thing left that you need to fulfill the task (I’ll come to that in a moment).

So let me first shortly explain which are the javascript “culprits” that cause the page to display the “list” context specific ribbon when you click on the web part (not on a particular item – that’s a different case) or check the web part’s selection checkbox at the top right corner. If you inspect closely the HTML of the page containing the XLV web part you will be able to find these two HTML elements:

<td id="MSOZoneCell_WebPartWPQ2" valign="top" class="s4-wpcell" onkeyup="WpKeyUp(event)" onmouseup="WpClick(event)">

 

<input type="checkbox" id="SelectionCbxWebPartWPQ2" class="ms-WPHeaderCbxHidden" title="Select or deselect test Web Part" onblur="this.className='ms-WPHeaderCbxHidden'" onfocus="this.className='ms-WPHeaderCbxVisible'" onkeyup="WpCbxKeyHandler(event);" onmouseup="WpCbxSelect(event); return false;" onclick="TrapMenuClick(event); return false;" />

The first one is one of the top container elements of the XLV web part – have a look at its “onmouseup” attribute – it is this javascript bit that triggers the selection of the web part and the appearing of the parent list’s contextual ribbon. The second element is the selection checkbox control itself. Notice the “id” attributes of these two HTML elements – they both have one and the same suffix – “WPQ2” – this is actually something like an web part index which in case you have more than one web parts on the page can be used to identify these two elements for a specific web part.

And let’s go directly to the custom XSLT that hides the ribbon when used in the XLV web part:

<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">

  <!-- import the standard main.xsl so that we have all standard stuff -->

  <xsl:import href="/_layouts/xsl/main.xsl"/>

  <!-- this template was copied from the standard vwstyles.xsl -->

  <xsl:template match="/">

    <!-- only this javascript block was added -->

    <script>

      try

      {

        // remove the click handler of the containing element

        $get('MSOZoneCell_WebPart<xsl:value-of select="$WPQ"/>').onmouseup = function (){};

        // remove the TD element containing the wp selection checkbox in the wp's header

        $get('SelectionCbxWebPart<xsl:value-of select="$WPQ"/>').parentNode.parentNode.style.display = 'none';

      }

      catch (ex) {}

    </script>

    <xsl:choose>

      <xsl:when test="$RenderCTXOnly='True'">

        <xsl:call-template name="CTXGeneration"/>

      </xsl:when>

      <xsl:when test="($ManualRefresh = 'True')">

        <xsl:call-template name="AjaxWrapper" />

      </xsl:when>

      <xsl:otherwise>

        <xsl:apply-templates mode="RootTemplate" select="$XmlDefinition"/>

      </xsl:otherwise>

    </xsl:choose>

  </xsl:template>

</xsl:stylesheet>

As you see, it is pretty concise and doesn’t need much explanation as to what it does and how it does it – I also put several comments inside it, so that you can get a better clue of its workings. Notice that the “WPQ” index that I mentioned above is actually available in an XSL parameter (with the same name) that the XLV web part has initialized for you, so the localizing of the two HTML elements in the javascript becomes a really easy task.

This snippet should be saved as a file in the TEMPLATE\LAYOUTS\XSL folder (I saved it as “main_noribbon.xsl”). After you have the XSL file you will need to set the XLV web part to use it – you have two options here – to set the XslLink property of the web part (the value in this case should be: /_layouts/xsl/main_noribbon.xsl) – you can do that easily using the SharePoint UI. The other option is to set the XslLink property of the associated hidden SPView of the web part (the value in this case is simply the name of the file: main_noribbon.xsl) – the standard UI can’t be used in this case, so you will need some lines of code here (see below).

And … you need one more thing to have it all working. Apart from the ability to select the whole web part, the XLV allows you to select individual item rows which also results in displaying the ribbon. To override this you won’t need extra XSL because the XLV web part provides this as an option out of the box – it is about the so called “view styles” that you can use for list views and hence XLV web parts. The view style can be changed easily from the standard UI – you click the “modify view” button in the ribbon (you have already disabled the ribbon? – no worries – you can do the same from the edit tool-pane of the XLV – it is the “Edit the current view” link there). In the standard “Edit View” page that will open you can scroll down and locate the “Style” section, expand it and then in the “View Styles” list box select the topmost option – “Basic Table”. This view style is pretty much the same as the “Default” one save the ability to select the item rows.

As you saw, you can apply the changes (once you have the XSL file in place) using the standard SharePoint UI alone. And here is how you can do it with code:

private static void XLVHideRibbon(SPWeb web, XsltListViewWebPart xlv)

{

    // if the page is in a library that requires check out, the file containing the XLV should be checked out before calling this method

 

    // get the hidden view of the XLV web part

    SPView hiddenView = web.Lists[new Guid(xlv.ListName)].Views[new Guid(xlv.ViewGuid)];

    // set its XslLink property to reference the custom "no ribbon" XSLT file

    hiddenView.XslLink = "main_noribbon.xsl";

 

    // web.ViewStyles[0] - Basic Table

    hiddenView.ApplyStyle(web.ViewStyles[0]);

    // update the hidden view

    hiddenView.Update();

}

Note that instead of setting the XslLink property of the XLV web part, I set the XslLink property of its hidden SPView, but this basically achieves the same effect.

Monday, October 18, 2010

ListViewWebPart – set the toolbar with code without reflection

[update (Aug 25, 2011): this solution works only for standard SharePoint lists, whose schema definitions (in the schema.xml file) already contain view definitions which do not have the toolbar (usually view definitions with BaseViewID=0) – see details below]

After several postings about the XLV web part in SharePoint 2010, let’s take a step back and have a look at its predecessor – the ListViewWebPart from SharePoint 2007. And more precisely about the problem with the list view toolbar when you add the web part with code. Basically the problem is that the web part gets added with the toolbar containing the “New”, “Actions” and “Settings” buttons which you normally see in the default list view pages like the “AllItems.aspx” one, and which in probably more than 90% of the cases you don’t want to see in the custom page, to which you are adding the web part.

There is already a solution to this problem (i.e. to remove the toolbar after you add the web part), which you can find in at least a dozen SharePoing blogs in internet, but the thing is that the solution in question uses reflection. Which is not a good thing at all. And actually the original solution stopped working after the Infrastructure update for MOSS 2007, so a small extra bit of reflection was to be added to the original code, so that it works after the update.

And let’s see now, how it is possible to add a ListViewWebPart to a page without the commands toolbar. Actually there are two slightly different ways to add a ListViewWebPart to a page with code, let’s see the first one:

        static void AddListView(SPFile file, SPList list, string zoneID)

        {

            SPLimitedWebPartManager mngr = file.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

            ListViewWebPart lvWebPart = new ListViewWebPart();

 

            lvWebPart.ListName = list.ID.ToString("B").ToUpper();

            // this is either the default view, or some of the other views that you get from the SPList.Views collection by name

            lvWebPart.ViewGuid = list.DefaultView.ID.ToString("B").ToUpper();

 

            mngr.AddWebPart(lvWebPart, zoneID, 1);

        }

Yes, this shouldn’t be something new for you, because you probably use something similar to this code if you have the problem with the commands toolbar.

And here is the second approach:

        static void AddListView2(SPFile file, SPList list, string zoneID)

        {

            SPLimitedWebPartManager mngr = file.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

            ListViewWebPart lvWebPart = new ListViewWebPart();

 

            lvWebPart.ListName = list.ID.ToString("B").ToUpper();

            string schemaXml = list.GetUncustomizedViewByBaseViewId(0).HtmlSchemaXml;

            // you can modify the schema XML of the view before assigning it to the ListViewWebPart

            // e.g. you can change the ViewFields, the Query element, etc

            lvWebPart.ListViewXml = schemaXml;

 

            mngr.AddWebPart(lvWebPart, zoneID, 1);

        }

As you see, it is very similar to the first approach and the only difference is that instead of setting the ListViewWebPart.ViewGuid property you set the ListViewWebPart.ListViewXml property. And the outcome of this code snippet is that the list view web part gets added to the target page without the annoying toolbar.

So far so good, but let’s have a closer look at the two code snippets, so that you can understand what happens behind the curtains. In the first code example, the ListViewWebPart.ViewGuid property gets set – but this doesn’t imply that the LV web part gets bound somehow to the SPView instance whose ID was provided to the web part’s property. In reality what happens is that a new SPView gets created when the web part is added to the page – this SPView is hidden but you can still find it in the SPList.Views collection of the parent list. What’s more – this SPView is always associated with this LV web part and actually they are more or less two representations of one and the same internal entity (check this older posting of mine for more details). Another detail here is that when this hidden SPView is created its schema XML (containing the settings for the view fields, query, toolbar etc) is copied from the source SPView, whose ID you provided to the ViewGuid property of the web part. And this is why you end up with the commands toolbar in your LV web part – normally the SPList.Views collection contains the views from the standard list view pages of the SharePoint list (like the “All Items” one, etc), all of which do have a commands toolbar.

And now, let’s see what happens in the second code sample – you see that instead of providing a SPView’s ID to the web part, a full view schema XML is retrieved and then provided to the web part’s ListViewXml property – as you see in this case you don’t need to set the ViewGuid property at all. Let’s first see how the view schema XML gets retrieved – you can see the SPList.GetUncustomizedViewByBaseViewId method – this method returns a SPView object but this SPView instance is not a normal view that you can get from the SPList.Views collection. This is sorts of artificial SPView instance whose view schema XML is populated directly from the list’s “schema.xml” file. In the schema.xml of the list’s template definition you have a “Views” element which contains several “View” elements each of which defines the available view schema XML-s for that list. Each “View” element has a unique identifier – that’s its “BaseViewID” attribute – and the integer parameter that you specify in the GetUncustomizedViewByBaseViewId method references the “BaseViewID” attribute. There is one other detail here – some of the “View” elements in the “schema.xml” file of the list definition do provision list views for the list instances created from this list definition (for example the “AllItems.aspx”), but there are also “View” elements that actually don’t provision list view pages:

<View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,objectiv_schema_mwsidcamlidC24;" DefaultView="TRUE" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/generic.png" Url="AllItems.aspx">

This is the “View” element with BaseViewID=1 of the schema.xml of the standard CustomList – as you see from its attributes it provisions the “AllItems.aspx” view page for the lists based on that list definition.

<View BaseViewID="0" Type="HTML">

And this is the “View” element (it has many child elements but I omitted them because the XML is huge) with BaseViewID=0. As you can see, it contains much less attributes and doesn’t provision any of the list’s view pages. But this doesn’t mean that the view schema XML that it defines is not used, on the contrary – for example, when you use the standard SharePoint UI to add LV web parts to a page, it is the view schema with BaseViewID=0 that’s get copied to the hidden SPView of the web part. And also in the code sample above I specified the view schema with BaseViewID=0 in the GetUncustomizedViewByBaseViewId method for the same reason. If you have a closer look at the “View” elements in the schema.xml you will see the difference in their “Toolbar” elements definition and will see why the “View” with BaseViewID=0 doesn’t add a commands toolbar and the “View” elements with different BaseViewID values normally do.

[update (Aug 25, 2011): the difference between setting the LVP’s ViewGuid and ListViewXml properties is that in the first case, the whole schema of the provided SPView will be copied and used in the newly created LVP, which means that in most cases you will end up with the standard toolbar, because it is already present in most of the non-hidden views (normal view pages) of the list. In the second case although you provide a full view schema, the SharePoint object model will take into account only its BaseViewID property and will otherwise use the original view definition with the same BaseViewID from the list’s schema.xml file. The view definition with BaseViewID=0 for most of the standard SharePoint list templates just happens to come without a toolbar, so this will help you solve the issue with the LVP’s toolbar. Apart from the toolbar, if you want to use a modified view definition when creating a new LVP – this won’t be possible at the moment of the provisioning itself (the only options that you have is to choose between the existing view definitions or the already existing views, as I explained above). But once you have the LVP created it is possible to modify the hidden SPView instance associated with the LVP (check the line of code at the bottom of the posting as to how it is possible to retrieve this hidden SPView from the web part). You can modify the hidden SPView using its properties which expose most of the view’s schema. If you are interested in changing the whole schema you can see this sample code, which unfortunately resorts to reflection, contrary to the posting’s title]

One other advantage of the second approach for adding a LV web part is that after you retrieve the view schema XML you can modify it before assigning it to the ListViewWebPart.ListViewXml property. For example you can change the ViewFields XML node or the Query node and even the Toolbar XML element if you want to display some custom toolbar of yours.

One last thing that I want to mention – it is about the case when you already have you LV web part added to the page and want to change its toolbar (the solution above was intended for the scenario when you add a new LV web part, which is actually the more common case). In this case I would simply delete the existing web part and add a brand new LV web part to the page. The trick here will be that instead of getting the view schema XML with the SPList.GetUncustomizedViewByBaseViewId method, I will use the schema (SPView.HtmlSchemaXml) from the existing web part (actually from its hidden SPView), before I delete it (then the view schema should be modified accordingly). Here is a small code snippet of how your can get the LV web part’s hidden SPView:

SPView hiddenView = list.Views[new Guid(lvWebPart.ViewGuid)];