1111

Saturday, September 18, 2010

XsltListViewWebPart & ContentByQueryWebPart – ParameterBindings, localization and embedded server controls

In SharePoint 2010 the XsltListViewWebPart and the ContentByQueryWebPart web parts play a pivotal role for the presentation of list data. It turns out that both web parts share a lot of common functionality because they both inherit one and the same base XSL transformations using web part – the DataFormWebPart (the CQWP via the CmsDataFormWebPart and the XLV via the BaseXsltListWebPart classes respectively). One of these shared pieces of functionality is the so called “parameter bindings” available through the DataFormWebPart.ParameterBindings property. The idea of the “parameter bindings” is that using specific XML data that you assign to the ParameterBindings property you can fetch various properties or variables from the asp.net, page, web parts, etc. environments which can then become available as XSL parameters (“xsl:param” XSL items) in your custom XSL that you use in either the XsltListViewWebPart or the ContentByQueryWebPart web parts. With these additional custom XSL parameters you can apply different presentation logic and even make the presentation of the two web parts react dynamically to the changes of the variables coming from these external (from the web part’s perspective) environments/contexts. So, let’s jump directly to a sample “parameter bindings” XML:

<ParameterBindings>

  <ParameterBinding Name="CustomBinding" DefaultValue="my custom value" />

  <ParameterBinding Name="dvt_firstrow" Location="Postback" DefaultValue="1" />

  <ParameterBinding Name="ResourceBinding" Location="Resource(wss,noitemsinview_doclibrary)" />

  <ParameterBinding Name="Today" Location="CAMLVariable" />

  <ParameterBinding Name="UserID" Location="CAMLVariable" />

  <ParameterBinding Name="WPVariableBinding" Location="WPVariable(_WPID_ | _WPQ_ | _WPR_ | _WPSRR_ | _LogonUser_ | _WebLocaleId_)" />

  <ParameterBinding Name="QuerySringBinding" Location="QueryString(myqueryparam)" DefaultValue="empty" />

  <ParameterBinding Name="FormBinding" Location="Form(myhidden)" DefaultValue="empty" />

  <ParameterBinding Name="ServerVariableBinding" Location="ServerVariable(REMOTE_ADDR)" />

  <ParameterBinding Name="WPPropertyBinding" Location="WPProperty(Title)" />

  <ParameterBinding Name="ControlBinding1" Location="Control(myDDL)" />

  <ParameterBinding Name="ControlBinding2" Location="Control(myDDL,SelectedIndex)" />

  <ParameterBinding Name="CombinedBinding" Location="Form(myhidden);QueryString(myqueryparam)" DefaultValue="empty" />

</ParameterBindings>

Yes, this was a somewhat extensive sample, but it actually covers most of the options that are available for the “parameter bindings” and I will give more details for each option shortly. As you see the XML’s format is pretty simple – it contains a root “ParameterBindings” element with “ParameterBinding” child elements. The “ParameterBinding” element may have three attributes: Name, Location and DefaultValue. The “Name” attribute is mandatory and it specifies the name of the XSL parameter that will be initialized by this “parameter binding”. In most cases you are free to specify any suitable and meaningful for your scenario name for the parameter binding, but for certain values of the “Location” attribute you can use only a predefined set of possible names (see below). The “Location” attribute contains the logic for specifying the context from which you will fetch a certain variable/value that will become available as an XSL parameter – you can choose from several predefined options here. The “DefaultValue” attribute is self explanatory – when the value retrieved from the specified context is not available (is an empty string), the XSL parameter will be initialized with the value of the “DefaultValue” attribute.

So, after you know the XML format of the ParameterBinding property, the next logical step would be to assign this XML to a real CQWP or XLV web part that you have somewhere in your SharePoint farm. The standard SharePoint UI doesn’t allow to edit this property, so you will have to choose from several other alternatives: SharePoint Designer 2010 (pretty handy actually and requires no custom coding), custom code in a feature receiver or a tool, PowerShell script or a specialized tool (a good place to advertise my “web part manager” utility).

In order that you can actually use the custom parameters in your XSL you will also need to define “xsl:param” elements with corresponding names in the main (root) XSL file of your CQWP or XLV web part (your custom ContentQueryMain.xsl for the CQWP or the Main.xsl for the XLV web part). If the “xsl:param” is defined in the main XSL file it will be available (visible) in all included XSL files as well. Basically you can define the “xsl:param” elements for your “parameter bindings” in one of the included XSL files (e.g. the ItemStyle.xsl or the fldtypes.xsl) – then the parameter will be available only in this XSL file (there won’t be a problem if the parameter is defined in both the main and the included XSL file either). The definitions of the XSL parameters for the parameter bindings from the sample above can look something like:

  <xsl:param name="CustomBinding" />

  <xsl:param name="ResourceBinding" />

  <xsl:param name="dvt_firstrow" select="1" />

  <xsl:param name="Today" />

  <xsl:param name="UserID" />

  <xsl:param name="WPVariableBinding" />

  <xsl:param name="QuerySringBinding" />

  <xsl:param name="FormBinding" />

  <xsl:param name="ServerVariableBinding" />

  <xsl:param name="WPPropertyBinding" select="'no name'" />

  <xsl:param name="ControlBinding1" />

  <xsl:param name="ControlBinding2" />

  <xsl:param name="CombinedBinding" />

Note that by using the “select” attribute of the “xsl:param” attribute you can specify again a default value for the parameter if the initializing parameter binding has no “DefaultValue” attribute and returns an empty value. This default value will also be used if there is no parameter binding with that name specified in the XML of the ParameterBindings property.

And now let’s have a look at the available options that you have for the “Location” attribute of the “PropertyBinding” element:

  • missing Location attribute – this is also possible – in this case you need to provide the “DefaultValue” attribute -basically you provide the web part with a fixed/constant parameter value. This can be very handy if you have for example several CQWP parts using several different but very similar item styles (the item styles can differ in some minor elements like the target of the links or the presence of some additional small elements per item). In this scenario you can use only one item style which can check one or several parameters provided by the parameter bindings and adjust its presentation accordingly (the different web parts will provide different values in their parameter bindings or not provide some of the parameter bindings at all). It is obviously far easier to maintain only one item style instead of several, so the parameter bindings can help a lot in this scenario.
  • Location “Postback” – in this case you cannot choose arbitrary names, there is a predefined set of available names and they all have the “dvt_” prefix: dvt_sortdir, dvt_sortfield, dvt_filterfields, dvt_firstrow, dvt_nextpagedata, dvt_prevpagedata, dvt_partguid. From these only the last one is available in the CQWP (the rest will always have empty values in the CQWP). The “dvt_partguid” parameter will contain a value that seems like the “ClientID” of the web part (a sample value will look something like ctl00$ctl23$g_59a84b77_6a94_4c65_81cd_618b4c21c374) but actually you won’t find a matching HTML element with exactly this ID on the page. The other “dvt_” parameters contain specific XLV “postback” data related to the XLV’s sorting, filtering and paging. In fact these are already defined as “xsl:param” elements in the standard Main.xsl of the XLV web part and get initialized by the web part even without it being necessary to add them as parameter bindings. Depending on the current sorting, filtering and paging of your XLV you can see values like these in the “dvt_” parameters:

    dvt_firstrow: 31
    dvt_sortdir: descending
    dvt_sortfield: LinkTitle
    dvt_filterfields: ;LinkTitle;
    dvt_nextpagedata: Paged=TRUE&p_Title=item%2045&p_ID=45
    dvt_prevpagedata: Paged=TRUE&PagedPrev=TRUE&p_Title=item%2071&p_ID=71
  • Location Recource([Resource_File],[Resource_Name] – this one is very useful since it allows you to make values from resource files available in your XLV or CQWP web parts. The resource files are located under the App_GlobalResources subfolder of the physical directory of your web application and have the “resx” extension. If you have for example this value in the “Location” attribute - Resource(wss,noitemsinview_doclibrary) – it will fetch a localized string from the “wss.resx” resource file (actually it can be a resx file for a specific culture like wss.en-US.resx) with a name “noitemsinview_doclibrary”. If you check the wss.resx file you will see that the value of this resource string is “There are no items to show in this view of the "<ListProperty Select="Title" HTMLEncode="TRUE" />" document library” – you see a fancy “ListProperty” XML element inside the resource string, but in the XLV this will get replaced with the title of the actual list that the XLV web part displays. Apart from the “parameter bindings” localization support there are other alternatives for localization in both the XLV and CQWP web parts – check the paragraph titled “Localization” below.
  • Location CAMLVariable – in this case you can use only two predefined values for the “Name” attribute of the parameter binding: Today and UserID. Sample values returned by these two parameters are:

    Today: 2010-09-17T16:04:48Z
    UserID: Stefan Stanev

    Note that the UserID parameter actually returns the display name, not the account name of the current user.
  • Location WPVariable([Tokens]) – the value inside the brackets can contain one or a combination of several predefined tokens: _WPID_, _WPQ_, _WPR_, _WPSRR_, _LogonUser_, _WebLocaleId_. If you use a combination of these you can separate them with spaces or other arbitrary characters/strings. The names of these tokens are not that descriptive but you can get some idea of their meaning by these sample values that correspond to the “WPVariable” parameter binding from the sample at the top:

    g_3242c920_b37c_4cfa_a356_955bb398d47f | WPQ2 | http://myserver/sites/2/_wpresources/Microsoft.SharePoint.Publishing/14.0.0.0__71e9bce111e9429c | /sites/2/_wpresources/Microsoft.SharePoint.Publishing/14.0.0.0__71e9bce111e9429c | myserver/stefanstanev | 1033
  • Location QueryString([QueryParam]) – this allows you to get values from query parameters in the current URL in your custom XSL – it actually maps to the HttpContext.Current.Request.QueryString collection. So if you have this query parameter in the URL of the current page: default.aspx?myqueryparam=somevalue – the value “somevalue” will become available in the xsl:param corresponding to the “QueryString” parameter binding which specifies the “myqueryparam” parameter name in its Location attribute.
  • Location Form([FormParam]) – this maps to the HttpContext.Current.Request.Form collection. If you have this input element on your page:

    <input type="hidden" id="myhidden" name="myhidden" value="myhiddenvalue"/>

    this parameter binding

    <ParameterBinding Name="FormBinding" Location="Form(myhidden)" DefaultValue="empty" />

    will provide the “myhiddenvalue” in the <xsl:param name="FormBinding" /> parameter (only on page postbacks, otherwise the parameter value will be the specified in the “DefaultValue” attribute: “empty”)
  • Location ServerVariable([ServerVariable]) – this maps to the HttpContext.Current.Request.ServerVariables collection. If you have for example Location=”ServerVariable(REMOTE_ADDR)” it will initialize the corresponding XSL parameter with something like: fe80::a589:57ce:e0ec:1f1c%13
  • Location WPProperty([PropertyName]) – in the brackets you can specify the name of a public instance property of the XLV or CQWP classes – this can be the “Title” or “ID” or any other property of the web parts whose value you want to use in your XSL.
  • Location Control([ControlID]) and Control([ControlID],[PropertyName]) – here you need to provide the ID of a server control on the page containing the web part and optionally the name of a public instance property of the control’s class. If you skip the property name option the parameter will be initialized with the value of the standard Control.Text property. So if you have this drop-down list control on your page:

    <asp:DropDownList runat="server" ID="myDDL" AutoPostBack="true">

      <asp:ListItem>Item 1</asp:ListItem>

      <asp:ListItem>Item 2</asp:ListItem>

      <asp:ListItem>Item 3</asp:ListItem>

      <asp:ListItem>Item 4</asp:ListItem>

    </asp:DropDownList>


    which has its third option selected, the parameter binding with Location="Control(myDDL)" will initialize its parameter with the value of “Item 3” (the “Text” property of the DropDownList control will return the same value as its “SelectedValue” property) and the one with Location="Control(myDDL,SelectedIndex)" will initialize its parameter with the value of “2” (the third item in the zero based “SelectedIndex” property). As you see this type of parameter bindings allows you to make the presentation of your CQWP and XLV web parts interact with the server controls that you may have in the containing page.
  • Location that is a combination of several options – in this case the Location options should be separated by semi-colon (from the sample at the top - Location="Form(myhidden);QueryString(myqueryparam)"). The ordering here is important – the preceding Location options will be evaluated first – from the example – if you have both the “myhidden” from parameter and the “myqueryparam” query parameter available in the current page, the parameter binding will return the value of the “myhidden” from parameter, because the “Form” option is placed before the “QueryString” option. The idea of this combining is that if one or more of the preceding options yield no value, then the next option which returns a value will be taken by the parameter binding and passed to its corresponding parameter.

Localization

Localization can be quite a serious issue with the availability of so many multi-lingual sites around. Fortunately as you saw the “parameter bindings” support provides a way to use localized resources in the CQWP and XLV web parts. Besides the “parameter bindings” there is one other much easier alternative to get resource strings in the XLV web part:

<xsl:value-of select="/dsQueryResponse/Rows/@resource.wss.fld_yes" />

yes, it is as simple as that – you just specify with XPath an attribute of the “/dsQueryResponse/Rows” element of the source XML. And you don’t have to set additionally some web part properties or define extra XSL parameters. As you see the name of the attribute follows a specific convention – it consists of three parts separated by dots – the first one is the constant “resource” prefix, the second one specifies the name of the targeted resource file (wss.resx in this case) and the third part is the name of the resource in the resource file. In the “vwstyles.xsl” and “fldtypes.xsl” XSL files of the XLV web part there’re XSL variables like the “$Rows” and “$thisNode” which map to the “/dsQueryResponse/Rows” and “/dsQueryResponse/Rows/Row” XML elements respectively. So, these alternative variants of the special “resource” attribute’s XPath are possible (from locations where you have these XSL variables available):

<xsl:value-of select="$thisNode/../@resource.wss.Thunmbnail"/>

<xsl:value-of select="$Rows/@resource.wss.ManualRefreshText"/>

And … the interesting thing – how do these resource strings come as attributes of the “/dsQueryResponse/Rows” element, after all you can have lots of resource files with hundreds of resource strings inside them. The answer is that the “resource” attributes are actually something like pseudo-attributes of the source XML. And actually there is no source XML at least in the form of a XML DOM container class like XmlDocument or XDocument. The trick is possible because the XLV uses a custom XPathNavigator (overriding the DataFormWebPart.GetXPathNavigator virtual method). This custom XPathNavigator queries directly the SPListItem data that the XLV web part retrieves from SharePoint and as an extra provides the “resource” pseudo-attribute support. The main reason to use a custom XPathNavigator is simple – performance (this way you skip the step of transforming the list item data to a DOM XML container like XmlDocument so that you can then use its default XPathNavigator, which additionally will not perform so well as the custom one).

And how about the CQWP – I kept mentioning just the XLV web part in the previous paragraph but not the CQWP. Unfortunately the “resource” pseudo-attribute “shortcut” is not available in the CQWP (the CQWP also uses a custom XPathNavigator but it is not the same as the one used by the XLV web part and doesn’t implement the “resource” attribute logic). Still, there is yet another alternative available for the CQWP web part – the idea is that the .NET XSL transformations implementation allows you to define a .NET class whose methods (rather static methods) can become available and be used from inside the XSL (check the XsltArgumentList.AddExtensionObject method in MSDN for more details). Such “extension” class can also be used in the CQWP web part but you will need to create a new class inheriting the CQWP class. The CQWP’s ModifyXsltArgumentList virtual method (which you will need to override) is the place where you can hook the custom extension object – check this nice article which describes in detail the whole procedure. And this article describes how this technique can be used to fetch resource strings into the web part’s XSL.

Embedded server controls

In my previous posting I demonstrated how you can embed server controls inside the XSL of the XLV web part. This functionality is provided by the base DataFormWebPart class, so it would be normal to expect that this will also work in the CQWP web part. But … it doesn’t (at least in most of the cases that I tried). It turns out that in the DataFormWebPart class there is a public virtual property named CanHaveServerControls, whose obvious purpose is to enable or disable the ability of the web part to parse and instantiate embedded server controls. This property is not overridden in the CQWP but its base implementation checks several other virtual properties which in the case of the CQWP return always (or in most cases) false. So the only possible work-around in this case (if you need embedded server controls in the CQWP that badly) is to simply subclass the CWQP. The inheriting class will just need to override the CanHaveServerControls property and will look something like:

    public class MyCQWP : ContentByQueryWebPart

    {

        public override bool CanHaveServerControls

        {

            get

            {

                return true;

            }

            set

            {

                base.CanHaveServerControls = value;

            }

        }

    }

4 comments:
Yansel said...

Hi Stefan:
Is the
XsltListViewWebPart extendable? That is to say, can i inherit from it?
I would like to render custom field properties and the user credentials (im trying to implement column-level permisions in sp2010)
Thanks in advance!
neoglez

Stefan Stanev said...

Hi Yansel,

unfortunately you cannot subclass (inherit) the XLV web part - its class is defined as "sealed". I was thinking of the possibility to use custom XSL to achieve column level permissions - for that you will need the value of the SPList.EffectiveBasePermissions property available in the XSL. I checked the $XmlDefinition XSL parameter but though it contains most of the source SPList's properties it doesn't contain this one. If I have time I will check several other things and if I find something I will let you know.

Yansel said...

Hi Stefan:
Thanks for taking the time (and so fast!). I implemented a custom Field type with a custom property (the MS bug on custom Field properties was a.p.i.t.a.) and it works for the Display, Edit and Delete Form (form pages) but of course the list view is handle by the XLV, and there is no way i can get it at the fldtypes_.xsl level becouse i can not get either the custom Field property (where it is stored the SPGroup that is suposed to view the field) neither the user´s role. Right now i´m trying with custom content types-list to see if i can hook some how into the view, but i must say there no much information, in fact i could only find such as deep topics in your blog. Thanks for giving back to the comunity!
Yansel

Tibod said...

Note that the targetted resource file in cannot contain any '.''s (dots). I looked this up in reflector and it searches for the next . it meets and determines the resource file name with that:

@resource.AB.C -> finds resource key C in file AB
@resource.A.B.C -> Tries to find resource file A, and fails if it does not exist
@resource.C -> finds resource key C in wss.resx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值