Posts Tagged ‘Data View Web Part

24
Sep
09

Filtering in SharePoint DVWPs with Multiple Lists in the Data Source

Another question from my contact form:

Hi Marc.

My problem in short is to get a filters to work on columns that holds values from linked tables. For instance I have a column with

xsl:value-of select="../../../Products/Rows/Row[@Title=current()/@ProductID]/@ProductGroup"

Am at a loss for a way to do it at this point. A hint would be greatly appreciated.

And my response…

I’d build the filtering mechanisms separately from the DVWP which displays the items. You can use any controls you like, but I usually use DVWPs. In those DVWPs, post back to the same page based on the user selections with the filter values on the Query String, something like this:

http://servername/sitepath/default.aspx?ProductID=X&ProductGroup=Y

Then in your DVWP below, add filtering logic into your row selects, like this:

<xsl:variable name="Rows" select="/dsQueryResponse/primaryList/Rows/Row[
    (@ProductID = $ProductID or string-length($ProductID) = 0) and
    (@ProductGroup = $ProductGroup or string-length($ProductGroup) = 0)
  ]"/>

At this point, you’re doing all of the filtering in your XSL yourself. One of the problems with the mumbo jumbo code that SharePoint Designer creates for filtering on column headers is that it is pretty difficult to alter. Keep in mind that SPD is essentially just a code generator and it has to leave "hooks" in for the types of things the you *might* ask it to do later. One caveat: once you start writing your own XSL, don’t ask SPD to do things through the dialogs, as it may well break things on you. (Not all dialogs will cause problems, but until you are very familiar with what each choice generates in the XSL, this is the best rule to follow.)

10
Sep
09

Roll Up Blog Posts in a Site Collection

I got a quick question today from a client asking how to roll up blog posts.  He wanted to display the latest blog posts from all of the blogs which executives have in the Site Collection, which is used as the organization’s Intranet.

He had the idea right, using a Data View Web Part (DVWP) with the DataSourceMode=”Crosslist” and the Webs Scope=”SiteCollection”.  However, he got trapped by a cut and paste gotcha.

Since he wanted to only show the latest blog posts, he needed to set the correct value for ServerTemplate.  The CAML he had copied had <Lists ServerTemplate=’104′></Lists>, which specifies lists which are based on the ‘Announcements list’ template.  The value for the ‘Blog posts’ template is <Lists ServerTemplate=’301′></Lists>.

The possible values for ServerTemplate can be found in this MSDN support article, and here they are listed for ease of use:

  • 100   Generic list (this means any Custom List)
  • 101   Document library
  • 102   Survey
  • 103   Links list
  • 104   Announcements list
  • 105   Contacts list
  • 106   Events list
  • 107   Tasks list
  • 108   Discussion board
  • 109   Picture library
  • 110   Data sources
  • 111   Site template gallery
  • 112   User Information list
  • 113   Web Part gallery
  • 114   List template gallery
  • 115   XML Form library
  • 116   Master pages gallery
  • 117   No-Code Workflows
  • 118   Custom Workflow Process
  • 119   Wiki Page library
  • 120   Custom grid for a list
  • 130   Data Connection library
  • 140   Workflow History
  • 150   Gantt Tasks list
  • 200   Meeting Series list
  • 201   Meeting Agenda list
  • 202   Meeting Attendees list
  • 204   Meeting Decisions list
  • 207   Meeting Objectives list
  • 210   Meeting text box
  • 211   Meeting Things To Bring list
  • 212   Meeting Workspace Pages list
  • 301   Blog Posts list
  • 302   Blog Comments list
  • 303   Blog Categories list
  • 1100   Issue tracking
  • 1200   Administrator tasks list
26
Aug
09

Preserving the Existing Query String When Building a Link in a DVWP

Another question came to me in email today that I thought was worth sharing, along with my answer.  The email referred to my post about Alpha Selection of List Items in a Data View Web Part (DVWP), but it’s relevant to many Middle Tier development tasks.

I hope this email comes to you in good spirits! I was messing around with your alpha selection and it works great except I have 1 more stipulation. I filter a dvwp by URL parameter for instance:

www.mysite.com/site/Pages/thispage.aspx?Type=Type1 OR

www.mysite.com/site/Pages/thispage.aspx?Type=Type2

And then I would like to be able to them choose the A-Z and when it filters it will keep the original URL, but as of right now it will drop the Type=Type1/Type2 and while it filters ‘A’ it filters ‘A’ for both Type1 and Type2. Is there a way that I can allow it to keep the Type parameter then add the Letters parameter to the URL for it to filter further?

Sure you can!  You can just parse out the existing Query String parameters and add them back onto the URL when you build it up.  If you set up a new parameter like so:

<ParameterBinding Name="QUERY_STRING" Location="ServerVariable(QUERY_STRING)" DefaultValue=""/>

then you have the existing Query String parameters available to you.  You’ll just want to be sure to strip out the parameters you want to add if they are already there, something like this:

<a href=”{$URL}?{substring-before($QUERY_STRING, ‘&amp;Letter’)}&amp;Letter={$ThisLetter}”>

(This simple example assumes that the Letter parameter will always be there.)

19
Aug
09

Two SharePoint Designer DVWP Best Practices

I find myself retyping these two of my DVWP best practices over and over again in some form or another in the MSDN SharePoint – Design and Customization forum, so I thought I’d capture them here for posterity.

SharePoint Designer Generates Errors

It’s not unusual when you are making quite a few changes to a DVWP in SharePoint Designer that Designer itself causes errors.  Usually it’s one of the the dreaded "non-specific errors" which basically tells you that "you just did something wrong" (but nothing else useful).  The key trick here is to use Ctrl-Z to get back to the last working state.

Depending on the number and complexity of changes that you make, SharePoint Designer can "get lost".  It’s basically a code generator, and especially if you’ve made changes to the code yourself, Designer can cause errors.

When things like this happen, you’re often best served by simply editing the code yourself.  My rule is that if I have made a change to the code directly, I give up on using the dialogs altogether, as the odds of using them causing a problem is very high.

Web Part Connections Don’t Do What You Want

Web Parts linked with Web Part Connections, by default, tend to show all items; the filtering occurs once you’ve made a choice.  At least in the forum, almost no one wants this to be the case with their DVWPs.

What I usually do is build my own connections by passing values on the Query String back to the same page and filtering based on those values.  It’s more controllable, especially if the functionality of your DVWP is likely to expand (and when doesn’t it?),  and has the added benefit of allowing the user to bookmark what they are really seeing rather than the default starting point for the page.

13
Aug
09

Show Numbers with a Different Locale in a SharePoint DVWP

Here’s a great little trick from an MSDN Forum thread I was trying to help out in.  Thanks to Playerkiller for this one.

Suppose you’d like just a single DVWP to display numbers (either currency or regular numbers) in a different locale than the host site.  This trick lets you do just that.

Prerequisites: Numbers are shown with US notation so: 1000.00

1. Open the page you need to customize with Sharepoint Designer. Switch to code view. Perform a search to find the tag xsl:decimal-format. Change the whole line with:

<xsl:decimal-format name=”European” decimal-separator=”,” grouping-separator=”.” minus-sign=”-” NaN=”error” />

2. Now, locate your <xsl:value-of select=”@URENBESTDEZPER”/> and replace it as follows:

<xsl:value-of select=”format-number(@URENBESTDEZPER, ‘€#.###,00;-€#.###,00′, ‘European’)”/>

You should have replaced your view. ;)

(I made a few small edits, but the work was all Playerkiller’s.)

29
Jul
09

Simple DVWP-Based Graphing in SharePoint

Anyone who follows my blog or knows me professionally knows that I love working with the Data View Web Part (DVWP) in SharePoint.  It’s the Swiss Army knife of Web Parts, it’s a dessert topping, it’s a floor wax.  Yes, it even can help you graph your data.

I’ve posted before about this technique for showing simple bar graphs using DVWPs, but I’ve just added a new demo page to our Sympraxis Consulting demo site based on a suggestion from Mauro Cardarelli.

Think about how a simple graph like this can enhance a dashboard for a project or a topic area by graphing things like discussion posts by topic, tasks by days overdue, or, as in the demo, a sales pipeline.

image

Using a simple DVWP, we can graph list data in slick ways.  By clicking one of the buttons, a new sort column is passed back to the page on the Query String and the graph changes to reflect the selected column.

This demo shows data from a single list, but using an AggregateDataSource, you can also graph data from multiple, related lists.

24
Jul
09

The "Right" Way to Join Two Lists In a DVWP

As long as I’ve been working with Data View Web Parts (DVWPs), I’ve pretty much always done my “joins” manually by diving into the XSL.  When I started working with DVWPs, there wasn’t much available to tell me how to do things, so I just made it up based on what worked.

Today I saw this video from Laura Rogers that showed the way you are probably “supposed” to do a “join” in your DVWP.  Take a look.  I may have once followed this route, but it would have been long ago!

A few observations on all of this:

  • When you create a Linked Source, what actually happens is that the information about the sources is saved into an XML file in the _catalogs/fpdatasources Document Library in the current site.  What this XML file contains is information about the SharePoint:AggregateDataSource in a udc:DataSource wrapper.   Each time you use the Linked Source, this information is copied in as the SharePoint:AggregateDataSource for your DVWP.  Think of it as a SharePoint:AggregateDataSource template.  If you can make the CAML more efficient, and you expect to reuse the Linked Source, you can do it here.
  • Upon initial creation of the DVWP from the Linked Source, there are templates in the DVWP named dvt_1, dvt_1.body, and dvt_1.rowview.  (Depending upon the other options you choose from there, you may end up with more dvt_1.* templates.)
  • Note that both Data Sources (in this case; there can be many) have the selectcommand=”&lt;View&gt;&lt;/View&gt;” by default, so there is no efficiency in the “join”.  I haven’t seen issues with this, even with thousands of rows, but folks often [virtually] look at me askance when I suggest doing the filtering after the items have been retrieved.  (I often reply with “I learned to program in 16k of memory, so this makes me blanch, too, but I just haven’t seen performance problems.)  If you expect that you will have large numbers of items in your lists, think about how you can add WHERE clauses into your CAML to reduce the number of items retrieved initially as I mention above.
  • When you add the first Joined Subview, what happens is that a second set of templates called dvt_2, dvt_2.body, and dvt_2.rowview are created in your DVWP. This set of templates is for the second Data Source (thus the names).
  • The “join” isn’t really a join at all.  The dvt_1.rowview template calls the dvt_2 template and the values of the columns which you specify in the Joined Subview dialog are used to filter the second Data Source, something like this:
    <xsl:call-template name="dvt_2">
    <xsl:variable name="dvt_ParentRow" select="current()" />
    <xsl:variable name="Rows" select="../../../Assignees/Rows/Row[@Title=$dvt_ParentRow/@Title]" />

You can watch all of these things happen if you follow along with Laura’s video and do the same things that she is doing in Split mode in SharePoint Designer.  At this point, you’re sort of where I end up when I do most of this manually.  However, there are some best practices which I follow which I thought I’d pass along:

  • Rename the templates from dvt_1.* and dvt_2.* to match your list names.  So, given the code above, I’d rename dvt_2 to Assignees, dvt_2.body to Assignees.body, and dvt_2.rowview to Assignees.rowview.  This makes following even your own code much easier, and it certainly ought to make more sense to someone else who gets stuck with it.
  • Rather than using the dvt_ParentRow logic above, call the dvt_2 template (which is called something like Assignees now, right?) with the columns you want to “join” on as parameters, something like this:
    <xsl:call-template name="dvt_2">
        <xsl:with-param name="Title" select="@Title"/>
    </xsl:call-template>

    and then the top of your dvt_2 template looks like this:

    <xsl:template name="dvt_2">
      <xsl:param name="Title""/>
      <xsl:variable name="Rows" select="../../../Assignees/Rows/Row[@Title=$Title]" /> 
  • This way, if you need to “join” (notice how I keep putting “join” in quotes) based on multiple columns or a calculation, you’re positioned for it.  Simply pass in the additional parameter(s) and add the logic to the filter in dvt_2.

Once you’re as familiar with DVWPs as I am, you may forego the dialogs, too, but hopefully this gives you some better ideas about what’s going on “under the hood”.

22
Jul
09

Date Arithmetic in SharePoint DVWPs

I posted last September about Date Calculations in a DVWP.  In that post, I pointed out Andy Lewis’ great post about how to do Filtering and Formatting with Date Values over at the Microsoft SharePoint Designer Team Blog.

I’ve been answering quite a few questions about date arithmetic over at the MSDN forums lately, so I though an updated post was in order to explain a little bit more about how you can use the templates that Andy provides.  Here are the steps I would suggest:

  • Download the date_templates.xsl file.
  • Put date_templates.xsl into your Site Collection.  I usually put my reusable XSL into /Style Library/XSL Style Sheets in MOSS, or I create a Document Library in the root site in WSS.
  • Include the templates in your DVWP.  Assuming that you’ve used the location above, add the <xsl:import> line into your DVWP immediately below the <stylesheet line, like so:
<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">
<xsl:import href="/Style Library/XSL Style Sheets/date_templates.xsl"/>
  • Simply call the templates, passing in the appropriate parameters.  For example, to call the getDateFromDays template:
<xsl:call-template name="getDateFromDays">
  <xsl:with-param name="paramBaseDate" select="@MyDateColumn"/>
  <xsl:with-param name="paramDelta">7</xsl:with-param>
</xsl:call-template>

To understand what you need to pass into each template, you’ll need to dive into the date_templates.xsl file a little, but Andy’s documented the XSL well.  Each template will require a set of parameters like getDateFromDays above.

19
Jul
09

Cascading Dropdown Columns in a SharePoint Form – Part 2

UPDATE 2009-08-26: We’ve translated this logic into our jQuery Library for SharePoint Web Services. I *strongly* suggest that you look at that as an option, as it is far more robust.  And free!

In my post last week entitled Cascading Dropdown Columns in a SharePoint Form – Part 1, I showed how to create cascading dropdowns, meaning that a choice in the first dropdown would change the options in the second dropdown.  The example I gave was from a client project which had over 20 options in the second dropdown.  Because of this, the required JavaScript was a bit complicated because of the way that SharePoint renders the control for more than 20 options.

I got some great questions about that post and I promised to post again with a simpler example with a working demo. I’ve created a new Demo site with a CascadingDropdowns demo page on our Sympraxis Consulting Web site (all WSS!).

Notes to the critics:  Sure, you could read the lists directly from the JavaScript.  But this method seems like it will be easier to follow for the average “middle tier” developer (or non-developer).  It’s also possible to simply embed the table of value relationships directly in the page (hard-wired), though I wouldn’t recommend it.  (Use lists for what they are good for!)

Hopefully the page will be self-explanatory, but since this is a new way to demo my code, I appreciate any feedback you might have about how to make it more useful.

Looking at the page at the above link should give you all of the details, but the JavaScript looks like this:

_spBodyOnLoadFunctionNames.push("SetUpCascading");

var Column1 = new Object();
var Column2 = new Object();
var savedColumn2Options = new Object();

function SetUpCascading() {
    // Find Column1 in the DOM (in this demo, the column is named "Region")
    Column1 = getTagFromIdentifierAndTitle("select","","Region");
    // Find Column2 in the DOM (in this demo, the column is named "State")
    Column2 = getTagFromIdentifierAndTitle("select","","State");
    // Attach the onchange event to Column1
    Column1.attachEvent('onchange', Column1Changed);
    // Call the onchange event to set the initial options for Column2
    Column1Changed();
}

function Column1Changed() {
    // Find the table with the Column1 / Column2 / ID information
    var Column1Table = document.getElementById("Column1Table");
    // Find all of the table rows
    var Column1TableRows = Column1Table.getElementsByTagName("TR");
    // See which Column2 options are allowed for the chosen Column1 option
    Column2Count = 0;
    // For each of the table rows...
    for (var i=0; i < Column1TableRows.length; i++) {
        // Get the table detail cells
        var Column1TableRowsDetails = Column1TableRows[i].getElementsByTagName("TD");
        // If the Region value in the table row matches the currently chosen Region
        if(Column1TableRowsDetails[1].innerHTML == Column1.options[Column1.selectedIndex].text) {
            // Add the option to the Column2 dropdown
            Column2.options[Column2Count] = new Option(Column1TableRowsDetails[0].innerHTML,
                Column1TableRowsDetails[2].innerHTML);
            // Increase the count of available options for Column2
            Column2Count++;
            // For this demo, set the background color of the Column2 cell to green
            Column1TableRowsDetails[0].style.backgroundColor = "green";
        // If the Region value in the table row doesn't match the currently chosen Region
        } else {
            // For this demo, set the background color of the Column2 cell to red
            Column1TableRowsDetails[0].style.backgroundColor = "red";
        }
    }
    // Set the length of the options array
    Column2.options.length = Column2Count;
    // If there aren't any available choices, then disable the Column2 dropdown
    if(Column2Count == 0) Column2.disabled = true;
     else Column2.disabled = false;
}

and the key templates in the DVWP look like this.  Note that the table must have a unique id, which is used by the JavaScript to find it in the page: table id="Column1Table".

<xsl:template name="dvt_1">
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
   
<table id="Column1Table" border="0" width="100%" cellpadding="2" cellspacing="0">
        <xsl:for-each select="$Rows">
            <xsl:sort select="@Title" order="ascending"/>
            <xsl:call-template name="dvt_1.rowview"/>
        </xsl:for-each>
   </table>
</xsl:template>

<xsl:template name="dvt_1.rowview">
   
<tr>
       
<td class="ms-vb">
            <xsl:value-of select="@Title"/>
       </td>
       
<td class="ms-vb">
            <xsl:value-of select="@Region"/>
       </td>
       
<td class="ms-vb">
            <xsl:value-of select="@ID"/>
       </td>
   </tr>
</xsl:template>
14
Jul
09

Cascading Dropdown Columns in a SharePoint Form – Part 1

UPDATE 2009-08-26: I’ve translated this logic into my jQuery Library for SharePoint Web Services. I *strongly* suggest that you look at that as an option, as it is far more robust.  And free!

UPDATE 2009-07-19: I’ve added a second post on this with a simpler example. It also contains a link to a demo page which shows how it all fits together.

Over in the MSDN SharePoint – Design and Customization Forum there are frequent questions about “cascading” dropdowns.  This means having two (or more) dropdowns “connected” based on the choices the user makes.  I dug out an example from a client project that demonstrates this technique fairly well so that I could refer folks to it.

In this example, the client had Divisions, each with a set of Branches.  We stored the Division / Branch pairs in a simple SharePoint Custom List in the root site of the Site Collection which had just those two columns.  (We actually used the Title column in the list for the Branch just to keep it really simple.  This list wasn’t one that anyone outside of the administrators was going to be looking at.)

We wanted to have the cascading work in the forms for a Document Library where each document would be categorized as belonging to a Division and Branch.  (The Division and Branch columns were both Lookup columns into lists at the Site Collection root.)  To make the Division and Branch columns cascade in the forms, we needed to have these pieces in place:

  • A hidden Data View Web Part (DVWP) which put the Division / Branch pairs into a table on the form for use by the
  • JavaScript to change the available values for Branch once the Division was selected.

We didn’t actually need to customize the form itself; we left the default List Form Web Part (LFWP).  The major reason for this was that we had upward of 30 Content Types that could be stored in the Document Library, and we wanted the Content Type selection to work the clean, default way.  All of the changes we made to the form were done with the client-side JavaScript.

Here are the main XSL templates from the hidden DVWP.  Note that there is nothing fancy about this DVWP except that I gave the table an id=”BranchesTable” so that I could locate it in the DOM with the JavaScript:

<xsl:template name="dvt_1">
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
   
<table id="DivisionTable">
    <xsl:for-each select="$Rows">
        <xsl:sort select="@Branch" order="ascending"/>
        <xsl:call-template name="dvt_1.rowview" />
    </xsl:for-each>
   </table>
</xsl:template>

<xsl:template name="dvt_1.rowview">
<tr>
<td>
      <xsl:value-of select="@Division"/></td>
<td>
      <xsl:value-of select="@Title"/>
    </td>
     
<td>
      <xsl:value-of select="@ID"/>
    </td>
  </tr>
</xsl:template>

Next was the JavaScript.  As I always do, I stored the JavaScript in a Document Library in the root site of the Site Collection called JavaScript.  This way I could reuse it anywhere I needed to.  There were a lot of other things I was doing in the JavaScript to make the form work more richly, but I’ve snipped out the pieces that are relevant to this specific task (hopefully not breaking anything in so doing!).

First, I needed to attach an onchange event to the Division column’s dropdown.  This code snippet was called as part of the form load with _spBodyOnLoadFunctionNames.push();

// Find the Branch column
var Branches = getTagFromIdentifierAndTitle("input","","Branch");
// Save the Branch choices so that we can rebuild as needed
var savedBranchesChoices = Branches.choices + "|";
// Find the Division column
Division = getTagFromIdentifierAndTitle("select","","Division");
// Attach the onchange event
Division.attachEvent('onchange', DivisionChanged);

Second, here’s the JavaScript to handle the onchange event.  A few notes that are important here:

  • This was an environment where Internet Explorer 6+ was the supported browser.  We didn’t need to worry about any other browsers working well with the JavaScript (though I don’t think I was doing anything that wouldn’t work in other browsers).
  • There were more than 20 Branches.  When this is the case, SharePoint generates the dropdown control for the column differently than if there are fewer than 20 values.  I won’t got into the details of this; suffice it to say that some of the peculiarities below are there to manage this difference.
function DivisionChanged() {
    var chosenDivision = Division.options[Division.selectedIndex].text;

    // Find the hidden table with the Division / Branch / ID information
    var DivisionTable = document.getElementById("DivisionTable");
    var DivisionsTableRows = DivisionsTable.getElementsByTagName("TR");
    BranchesChoices = savedBranchesChoices;
    // See which choices ought to remain for this Division
    var newChoicesList = '';
    while(BranchesChoices.length > 0) {
        // Grab the first choice in the list
        var thisBranch = BranchesChoices.substring(0, BranchesChoices.indexOf("|"));
        // Trim the first choice out of the choices
        BranchesChoices = BranchesChoices.substring(thisBranch.length + 1);
        // Grab this choice's index
        var thisBranchIndex = BranchesChoices.substring(0, BranchesChoices.indexOf("|"));
        // Trim the first index out of the choices
        BranchesChoices = BranchesChoices.substring(thisBranchIndex.length + 1);
        // See if this Branch is in the table for this Division
        for (var j=0; j < DivisionsTableRows.length; j++) {
            var DivisionsTableRowsDetails = DivisionsTableRows[j].getElementsByTagName("TD");
            if(DivisionsTableRowsDetails[0].innerHTML == chosenDivision &&
                    DivisionsTableRowsDetails[2].innerHTML == thisBranchIndex)
                newChoicesList = newChoicesList + thisBranch + "|" + thisBranchIndex + "|";
        }
    }
    // The choices now become the filtered list, allowing for no selection: (None)
    if(newChoicesList.length > 0)
        Branches.choices = "(None)|0|" + newChoicesList.substring(0, newChoicesList.length - 1);
    else
        Branches.choices = "(None)|0";        
    Branches.value = "";
}



 

January 2010
M T W T F S S
« Dec    
 123
45678910
11121314151617
18192021222324
25262728293031

Twitter Updates