Monday, January 25, 2010

Fast Track to SharePoint Data View Web Parts

Whenever you need a viewer in SharePoint to show data from other sites, site-collections, web-services, XML files or databases, the Data Form Web Part (DFWP/DVWP) makes this task relatively simple. Use SharePoint Designer to add a data view web-part and configure it using parameters, paging, sorting and filters. Configure all needed aspects using SPD, set e.g. the item limit using "Common Data View Tasks" as doing so from code later requires more work.

When the data viewer is done, export the finished result to a .webpart file and package it into a feature as any other web-part. I recommend extracting the XSL stylesheet template from the <XSL> element into a file using SPD, and store it in the Style Library and use the XslLink web-part property to reference it.

The main drawback with this simple approach is that the users will have to use the parameters XML editor to change the web-part settings instead of having nice SharePoint tool pane input boxes. Another significant issue is that the data source configuration will be hardcoded. This is especially bad when using web-services, as the web-service URL stored in DataSourcesString selectUrl must be an absolute URL. Thus, you cannot implement, test and deploy the web-part on different hosts without manually changing the value of selectUrl in the .webpart file and recompiling your WSP.

However, there is a simple solution to both problems: subclassing the DataFormWebPart class. As Phil Wicklund shows in Writing your own custom DFWP, this is not hard at all. In addition, his source code on CodePlex shows how to create user friendly tool pane properties and connect it to the DFWP configuration. Note that DFWP is a v3 web-part that inherits from the System.Web.UI.WebControls.WebParts.WebPart class. Thus, ASP.NET 2.0 attributes such as [WebBrowsable] must be used for a property to be displayed in the tool pane.

Add a C# class to your feature and inherit the Microsoft.SharePoint.WebPartPages.DataFormWebPart class in your custom web-part class. All you need to change in your existing web-part configuration is the type name, assembly, version, culture and public key string in the <webPart><metaData> XML.

Remove the DataSourcesString property from your webpart file and set it directly from code instead. If you don't move the data source into code, then DFWP will try to access the web-service before you have a chance to correct the URL. This will most likely result in a HTTP 401 authentication failed or a HTTP 404 not found error. Override the HandleRuntimeException and HandleXslException methods to log and inspect these kind of DFWP errors.

Correcting the hardcoded web-service URL is also quite easy once you've got your own C# web-part to access and override the DataFormWebPart standard behaviour:

protected override void SetDataSourceProperties()
string dataSource = _dataSourcesString;
string asmxRoot = SPContext.Current.Site.Url + "/_vti_bin/";
this.DataSourcesString = dataSource.Replace("http://puzzlepart/_vti_bin/", asmxRoot);

this.ParameterValues.Set("LinkGroupName", _linkGroupName);

catch (Exception ex)
Label lblError = new Label{ Text = ex.ToString() };

This code just performs a replace on the hardcoded selectUrl pointing to a SharePoint web-service on my dev box to change the host to the current site-collection root URL. The same approach must be used to set the LoginName of the current user in the data source string. The custom data viewer is now safely deployable to any site, totalling less than 40 lines of code.

Use the ParametersValue collection to change the parameters passed to the XSL. Note how these parameters are separate from those of the data source. The only safe method I've found for applying web-part tool pane properties to e.g. DFWP filtering, is SetDataSourceProperties. OnInit is too early because of viewstate, OnPreRender is to late to for changed parameter bindings to have effect.

Other web-parts such as the Content Query Web Part (CQWP) can of course also be subclassed.

Note how you can emit information about exceptions to the UI by dynamically adding a label to the Controls collection. This saves you from looking at the log files or attach a debugger to diagnose problems.

1 comment:

Hans said...

Nice solution. Elegant and simple.