locked
Freeze Header Row and First Columns in a List RRS feed

  • Question

  • I have a list with a huge amount of data. I would like the header row (with the titles of the columns) and the first four columns to remain frozen while the user scrolls through the content. I know that Datasheet View freezes the header row, but I need the Standard View to do so. 

    I am using SharePoint Foundation 2010. I can perform modifications via SPD (and VS if necessary). I have reviewed this solution: http://kjellsj.blogspot.com/2009/06/sharepoint-jquery-scrolling-view-with.html, but it breaks with 2010 and IE8. I also reviewed this solution: http://archive.plugins.jquery.com/plugin-tags/fixed-header, but I'm not sure if that solution will work, or - if it will - how to begin.

    Any help is greatly appreciated!

    Update (12-29-11):
    I want to be able to freeze the row header and the first X columns in a list (or actually, in specific views of a list). I know that I can accomplish that with a standard table in a standard HTML page using the code from http://fixedheadertable.com/.

    So, now I am trying to determine how to integrate that code with my list. I figure I can add the jQuery via a reference in my Master page (I have found a few references on how to do that). But, I'm not still not sure of the steps. 

    More significantly, I have muliptle views of my list. I know that I can modify the code of each view's aspx page in SPD. Since each view is different, I figure that I must add view-specific code to each view's aspx page. But how? where? 

    I am sure that I am over-complicating things, but I just don't see how to get from http://fixedheadertable.com/ to making it work in SharePoint.

    Thanks very much for your help!!


    Daniel Keith
    • Edited by Daniel Keith Thursday, December 29, 2011 8:21 PM
    Wednesday, December 28, 2011 4:57 PM

Answers

  • Hello Daniel,

    The general idea is to separate the content to individual <table> or <div> tags, then use CSS (overflow-x:scroll) to make certain table rows to be fixed or have a scroll bar.

    Here is a solution for a similar project.

    -----------------------------------
    Requirement:
    Customer requires a DFWP to have two parts. The left side should be a fixed width and should not be part of the overall scrolling experience. In other words, it should behave like an Excel spreadsheet where certain columns are fixed.

    Solution:
    1. Create a basic DFWP. I created one against a custom list. No additional columns are needed for now. This can be made much more complicated later.
    2. Add the title column and created by column.
    3. Enable filtering at the table heading level so we have all the templates we want in place.
    4. This is what the general XSL look like:

    <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:output method="html" indent="no"/>
    <xsl:param name="dvt_adhocmode">sort</xsl:param>
    <xsl:decimal-format NaN=""/>

    <xsl:param name="dvt_apos">&apos;</xsl:param>
    <xsl:param name="dvt_fieldsort" />
    <xsl:param name="dvt_filterfield" />
    <xsl:param name="dvt_sortdir">ascending</xsl:param>
    <xsl:param name="dvt_sortfield" />
    <xsl:param name="dvt_sorttype">text</xsl:param>
    <xsl:param name="dvt_filterfields" />
    <xsl:param name="dvt_partguid" />
    <xsl:variable name="dvt_1_automode">0</xsl:variable>

    <xsl:template match="/">
    <xsl:call-template name="dvt_1"/>
    </xsl:template>
    <xsl:template name="dvt_1">
    <xsl:variable name="dvt_StyleName">Table</xsl:variable>
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
    <table border="0" width="100%" cellpadding="2" cellspacing="0">
    <tr valign="top">
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <th class="ms-vh" width="1%" nowrap="nowrap"></th>
    </xsl:if>
    <th class="ms-vh" nowrap="nowrap">
    <xsl:call-template name="dvt.headerfield" ddwrt:atomic="1">
    <xsl:with-param name="fieldname">@Title</xsl:with-param>
    <xsl:with-param name="fieldtitle">Title</xsl:with-param>
    <xsl:with-param name="displayname">Title</xsl:with-param>
    <xsl:with-param name="sortable">1</xsl:with-param>
    <xsl:with-param name="fieldtype">x:string</xsl:with-param>
    </xsl:call-template>
    </th>
    <th class="ms-vh" nowrap="nowrap">
    <xsl:call-template name="dvt.headerfield" ddwrt:atomic="1">
    <xsl:with-param name="fieldname">@Author</xsl:with-param>
    <xsl:with-param name="fieldtitle">Created By</xsl:with-param>
    <xsl:with-param name="displayname">Created By</xsl:with-param>
    <xsl:with-param name="sortable">1</xsl:with-param>
    <xsl:with-param name="fieldtype">x:string</xsl:with-param>
    </xsl:call-template>
    </th>
    </tr>
    <xsl:call-template name="dvt_1.body">
    <xsl:with-param name="Rows" select="$Rows"/>
    </xsl:call-template>
    </table>
    </xsl:template>
    <xsl:template name="dvt_1.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:sort select="*[name() = $dvt_sortfield] | @*[name() = $dvt_sortfield] | text()[name(ancestor::*[1]) = $dvt_sortfield]" order="{$dvt_sortdir}" data-type="{$dvt_sorttype}" />
    <xsl:call-template name="dvt_1.rowview"/>
    </xsl:for-each>
    </xsl:template>
    <xsl:template name="dvt_1.rowview">
    <tr>
    <xsl:if test="position() mod 2 = 1">
    <xsl:attribute name="class">ms-alternating</xsl:attribute>
    </xsl:if>
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <td class="ms-vb" width="1%" nowrap="nowrap">
    <span ddwrt:amkeyfield="ID" ddwrt:amkeyvalue="ddwrt:EscapeDelims(string(@ID))" ddwrt:ammode="view"></span>
    </td>
    </xsl:if>
    <td class="ms-vb">
    <xsl:value-of select="@Title"/>
    </td>
    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>
    </tr>
    </xsl:template>
    <xsl:template name="dvt.headerfield">
    <xsl:param name="fieldname" />
    <xsl:param name="fieldtitle" />
    <xsl:param name="displayname" />
    <xsl:param name="sortable">1</xsl:param>
    <xsl:param name="fieldtype">0</xsl:param>
    <xsl:choose>
    <xsl:when test="($dvt_adhocmode = 'sort' or $dvt_fieldsort = '1')and $sortable='1'">
    <xsl:variable name="sortfield">
    <xsl:choose>
    <xsl:when test="substring($fieldname, string-length($fieldname) - 5) = '(text)'">
    <xsl:value-of select="substring($fieldname, 1, string-length($fieldname) - 6)" />
    </xsl:when>
    <xsl:when test="substring($fieldname, 1, 1) = '@'">
    <xsl:value-of select="substring($fieldname, 2)" />
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="$fieldname" />
    </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="linkdir">
    <xsl:choose>
    <xsl:when test="$dvt_sortfield = $sortfield and $dvt_sortdir = 'ascending'">descending</xsl:when>
    <xsl:otherwise>ascending</xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="sortText">
    <xsl:choose>
    <xsl:when test="$linkdir='descending'">&apos; + &apos;descending&apos; + &apos;</xsl:when>
    <xsl:otherwise>&apos; + &apos;ascending&apos; + &apos;</xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="separator" select="' '" />
    <xsl:variable name="connector" select="';'" />
    <table CtxNum="1" cellspacing="0" class="ms-unselectedtitle" onmouseover="OnMouseOverAdHocFilter(this, '{concat($displayname,$separator,$fieldname, $separator,$fieldtype, $connector, 1033, $separator, $dvt_partguid)}')">
    <tr><td width="100%" class="ms-vb" nowrap="nowrap">
    <a>
    <xsl:attribute name="href">javascript: <xsl:value-of select="ddwrt:GenFireServerEvent(concat('dvt_sortfield={',$sortfield,'};dvt_sortdir={',$sortText,'}'))" />;</xsl:attribute>
    <xsl:attribute name="onclick">javascript: <xsl:value-of select="ddwrt:GenFireServerEvent(concat('dvt_sortfield={',$sortfield,'};dvt_sortdir={',$sortText,'}'))" />;</xsl:attribute>
    <xsl:choose>
    <xsl:when test="$fieldtype = 'Attachments'">
    <xsl:value-of select="$fieldtitle" disable-output-escaping="yes" />
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="$fieldtitle" />
    </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="$dvt_sortfield = $sortfield">
    <xsl:choose>
    <xsl:when test="$dvt_sortdir = 'ascending'">
    <img border="0" alt="Ascending" src="{ddwrt:FieldSortImageUrl('Desc')}" /></xsl:when>
    <xsl:when test="$dvt_sortdir = 'descending'">
    <img border="0" alt="Descending" src="{ddwrt:FieldSortImageUrl('Asc')}" /></xsl:when>
    </xsl:choose>
    </xsl:if>
    </a>
    <xsl:if test="contains($dvt_filterfields, concat($fieldname, ';' ))">
    <IMG SRC="/_layouts/images/filter.gif" BORDER="0" ALT="" />
    </xsl:if>
    </td>
    <td><img src="/_layouts/images/blank.gif" width="13" style="visibility: hidden" alt="" /></td>
    </tr>
    </table>

    </xsl:when>
    <xsl:otherwise>
    <xsl:choose>
    <xsl:when test="$fieldtype = 'Attachments'">
    <xsl:value-of select="$fieldtitle" disable-output-escaping="yes" />
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="$fieldtitle" />
    </xsl:otherwise>
    </xsl:choose>
    </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="$dvt_filterfield=$fieldname" ddwrt:cf_ignore="1"><img alt="Filter" src="{ddwrt:FieldFilterImageUrl('')}" /></xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    5. The first part of the custom XSL is to copy the table that houses the guts of the DFWP. This is our building block for the project. It contains the table, table rows, table headings and the call to the dvt_1.body template.

    The quickest way I can find the housing table is to put your cursor inside of the first table heading cell. In code view you should see an opening tag for the table. Place your cursor in front of the <table and press Control + Shift + Semicolon. This should select the whole table from opening to closing tag. Paste it below the closing tag of the first table.

    Here is a sample of what you're copying:

    <table border="0" width="100%" cellpadding="2" cellspacing="0">
    <tr valign="top">
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <th class="ms-vh" width="1%" nowrap="nowrap"></th>
    </xsl:if>
    <th class="ms-vh" nowrap="nowrap">Title</th>
    <th class="ms-vh" nowrap="nowrap">Created By</th>
    </tr>
    <xsl:call-template name="dvt_1.body">
    <xsl:with-param name="Rows" select="$Rows"/>
    </xsl:call-template>
    </table>

    I'm going to do a demo based on a 800 pixel wide DFWP. I mention this now so that you can go into the table element and change the width from 100% to 300px.

    Make the second table 500px. Adjust this to fit your needs.

    Once this is done you will have two tables on your page. The top one will be 300 px and the bottom one will be 500px.

    6. Place a <div> tag around each table. Give the top table a div id of Fixed and the bottom one Scroll. We'll use these later to create our CSS.

    <div id="Fixed"><table>Bunch of stuff</table></div>

    <div id="Scroll"><table>Bunch of stuff</table></div>

    7. Finally, create another div tag that houses both of the divs we just created and their content. Give it an id of container.

    <div id="Container">
    <div id="Fixed"><table>Bunch of stuff</table></div>

    <div id="Scroll"><table>Bunch of stuff</table></div>
    </div>

    8. Up to this point the view should look the same. Only big difference you might notice is the quick tag selector has extra divs inside of it.

    Place your cursor in the table heading in the bottom data view. This should bring you to the second table that is 500px wide, per this example. Find the following chunk of code:

    <xsl:call-template name="dvt_1.body">
    <xsl:with-param name="Rows" select="$Rows"/>
    </xsl:call-template>

    Change the template name to dvtScroll.body. In code view perform a search for dvt_1.body. This should help you find the actual template we're calling. We need to make a copy of this template and paste it below the existing one. Here is what you should be copying:

    <xsl:template name="dvt_1.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:call-template name="dvt_1.rowview"/>
    </xsl:for-each>
    </xsl:template>

    We'll need to change the templates name we just copied to match the second tables name. So the finished product should look like this:

    <xsl:template name="dvtScroll.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:call-template name="dvt_1.rowview"/>
    </xsl:for-each>
    </xsl:template>


    9. Next, we need to make a copy of the dvt_1.rowview template. In our recently renamed dvtScroll.Body template control click the dvt_1.roview. Use the Control + Shift + Semicolon trick to select the dvt_1.rowview template and paste the whole template below the existing one. This is what the code should look like:

    <xsl:template name="dvt_1.rowview">
    <tr>
    <xsl:if test="position() mod 2 = 1">
    <xsl:attribute name="class">ms-alternating</xsl:attribute>
    </xsl:if>
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <td class="ms-vb" width="1%" nowrap="nowrap">
    <span ddwrt:amkeyfield="ID" ddwrt:amkeyvalue="ddwrt:EscapeDelims(string(@ID))" ddwrt:ammode="view"></span>
    </td>
    </xsl:if>
    <td class="ms-vb">
    <xsl:value-of select="@Title"/>
    </td>
    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>
    </tr>
    </xsl:template>

    The finished product will look like this:

    <xsl:template name="dvtScroll.rowview">
    <tr>
    <xsl:if test="position() mod 2 = 1">
    <xsl:attribute name="class">ms-alternating</xsl:attribute>
    </xsl:if>
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <td class="ms-vb" width="1%" nowrap="nowrap">
    <span ddwrt:amkeyfield="ID" ddwrt:amkeyvalue="ddwrt:EscapeDelims(string(@ID))" ddwrt:ammode="view"></span>
    </td>
    </xsl:if>
    <td class="ms-vb">
    <xsl:value-of select="@Title"/>
    </td>
    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>
    </tr>
    </xsl:template>

    Don't forget to update the dvtScroll.Body template to call the new dvtScroll.rowview. Here is the finished product:

    <xsl:template name="dvtScroll.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:call-template name="dvtScroll.rowview"/>
    </xsl:for-each>
    </xsl:template>

    10. Now, it's a matter of adding table headings and removing table headings in the two distinct tables. Since the overall project is still contained in the dvt_1 template we should have the same rows variable and in turn the same content between the two web parts.

    For this simplistic data view with only two columns it should be easy to clean up.

    Start by placing your cursor in the top data view and remove the created by table heading. If you have filtering enabled the markup will be slightly more complex but the same principal applies.

    Also, I would recommend having a separate data view if you need to add the table headings with filtering. The extra data view is just there to have SPD generate the <th> markup so you don't have to do it by hand. If you know how to do it by hand more power to you.

    <th class="ms-vh" nowrap="nowrap">Created By</th>

    11. Next, go into the dvt_1.rowview template and remove the <td> that contains the Created by column. It's going to be @Author.

    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>

    Note:
    ================
    The only thing that gets these out of alignment is columns that span more than one row. Rich text columns are the main one that gave me some guff. I opted to substring the value so it would only display 20 characters and then ... I made it a hyperlink so if somebody wanted to view the full content it would pass a URL query string to the current page and a display form would show the information. This is totally an aside though and probably could be done with modal windows or any other HTML magic you feel like implementing.
    ================

    12. Finally, remove the extra columns in the second table. The second table should only have the table heading for created by and the table data for created by.

    To summarize, the first table will have the Title heading and Title column data and the second table will house Created by.

    13. We're ready to apply some CSS to make this look like one data view. This is what I came up with. Place this on the page somewhere or reference it via a CSS file. The CSS seems to work on IE, FF and Safari.

    <style type="text/css">
    #Container{width:800px;position:relative;padding:0px;margin:0px;border:0px;}
    #Fixed{width:300px;position:relative;padding:0px;margin:0px;border:0px;}
    #Scroll{width:500px;position:absolute;top:0px;left:300px;padding:0px;margin:0px;border:0px;overflow-x:scroll;}
    </style>

    ------------------

    Regards,

    Jing Wang | Microsoft Online Community Support

     

    • Marked as answer by Daniel Keith Thursday, January 5, 2012 5:33 PM
    Thursday, January 5, 2012 5:27 PM

All replies

  • Hi,

     

    Thank you for your question .I am trying to involve someone familiar with this topic to further look at this issue.

     

    Thanks

    Entan Ming

    TechNet Community Support

    Friday, December 30, 2011 9:56 AM
  • Thanks Entan. You're help is greatly appreciated!
    Daniel Keith
    Friday, December 30, 2011 11:11 PM
  • Hello Daniel,

    The general idea is to separate the content to individual <table> or <div> tags, then use CSS (overflow-x:scroll) to make certain table rows to be fixed or have a scroll bar.

    Here is a solution for a similar project.

    -----------------------------------
    Requirement:
    Customer requires a DFWP to have two parts. The left side should be a fixed width and should not be part of the overall scrolling experience. In other words, it should behave like an Excel spreadsheet where certain columns are fixed.

    Solution:
    1. Create a basic DFWP. I created one against a custom list. No additional columns are needed for now. This can be made much more complicated later.
    2. Add the title column and created by column.
    3. Enable filtering at the table heading level so we have all the templates we want in place.
    4. This is what the general XSL look like:

    <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:output method="html" indent="no"/>
    <xsl:param name="dvt_adhocmode">sort</xsl:param>
    <xsl:decimal-format NaN=""/>

    <xsl:param name="dvt_apos">&apos;</xsl:param>
    <xsl:param name="dvt_fieldsort" />
    <xsl:param name="dvt_filterfield" />
    <xsl:param name="dvt_sortdir">ascending</xsl:param>
    <xsl:param name="dvt_sortfield" />
    <xsl:param name="dvt_sorttype">text</xsl:param>
    <xsl:param name="dvt_filterfields" />
    <xsl:param name="dvt_partguid" />
    <xsl:variable name="dvt_1_automode">0</xsl:variable>

    <xsl:template match="/">
    <xsl:call-template name="dvt_1"/>
    </xsl:template>
    <xsl:template name="dvt_1">
    <xsl:variable name="dvt_StyleName">Table</xsl:variable>
    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
    <table border="0" width="100%" cellpadding="2" cellspacing="0">
    <tr valign="top">
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <th class="ms-vh" width="1%" nowrap="nowrap"></th>
    </xsl:if>
    <th class="ms-vh" nowrap="nowrap">
    <xsl:call-template name="dvt.headerfield" ddwrt:atomic="1">
    <xsl:with-param name="fieldname">@Title</xsl:with-param>
    <xsl:with-param name="fieldtitle">Title</xsl:with-param>
    <xsl:with-param name="displayname">Title</xsl:with-param>
    <xsl:with-param name="sortable">1</xsl:with-param>
    <xsl:with-param name="fieldtype">x:string</xsl:with-param>
    </xsl:call-template>
    </th>
    <th class="ms-vh" nowrap="nowrap">
    <xsl:call-template name="dvt.headerfield" ddwrt:atomic="1">
    <xsl:with-param name="fieldname">@Author</xsl:with-param>
    <xsl:with-param name="fieldtitle">Created By</xsl:with-param>
    <xsl:with-param name="displayname">Created By</xsl:with-param>
    <xsl:with-param name="sortable">1</xsl:with-param>
    <xsl:with-param name="fieldtype">x:string</xsl:with-param>
    </xsl:call-template>
    </th>
    </tr>
    <xsl:call-template name="dvt_1.body">
    <xsl:with-param name="Rows" select="$Rows"/>
    </xsl:call-template>
    </table>
    </xsl:template>
    <xsl:template name="dvt_1.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:sort select="*[name() = $dvt_sortfield] | @*[name() = $dvt_sortfield] | text()[name(ancestor::*[1]) = $dvt_sortfield]" order="{$dvt_sortdir}" data-type="{$dvt_sorttype}" />
    <xsl:call-template name="dvt_1.rowview"/>
    </xsl:for-each>
    </xsl:template>
    <xsl:template name="dvt_1.rowview">
    <tr>
    <xsl:if test="position() mod 2 = 1">
    <xsl:attribute name="class">ms-alternating</xsl:attribute>
    </xsl:if>
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <td class="ms-vb" width="1%" nowrap="nowrap">
    <span ddwrt:amkeyfield="ID" ddwrt:amkeyvalue="ddwrt:EscapeDelims(string(@ID))" ddwrt:ammode="view"></span>
    </td>
    </xsl:if>
    <td class="ms-vb">
    <xsl:value-of select="@Title"/>
    </td>
    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>
    </tr>
    </xsl:template>
    <xsl:template name="dvt.headerfield">
    <xsl:param name="fieldname" />
    <xsl:param name="fieldtitle" />
    <xsl:param name="displayname" />
    <xsl:param name="sortable">1</xsl:param>
    <xsl:param name="fieldtype">0</xsl:param>
    <xsl:choose>
    <xsl:when test="($dvt_adhocmode = 'sort' or $dvt_fieldsort = '1')and $sortable='1'">
    <xsl:variable name="sortfield">
    <xsl:choose>
    <xsl:when test="substring($fieldname, string-length($fieldname) - 5) = '(text)'">
    <xsl:value-of select="substring($fieldname, 1, string-length($fieldname) - 6)" />
    </xsl:when>
    <xsl:when test="substring($fieldname, 1, 1) = '@'">
    <xsl:value-of select="substring($fieldname, 2)" />
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="$fieldname" />
    </xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="linkdir">
    <xsl:choose>
    <xsl:when test="$dvt_sortfield = $sortfield and $dvt_sortdir = 'ascending'">descending</xsl:when>
    <xsl:otherwise>ascending</xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="sortText">
    <xsl:choose>
    <xsl:when test="$linkdir='descending'">&apos; + &apos;descending&apos; + &apos;</xsl:when>
    <xsl:otherwise>&apos; + &apos;ascending&apos; + &apos;</xsl:otherwise>
    </xsl:choose>
    </xsl:variable>
    <xsl:variable name="separator" select="' '" />
    <xsl:variable name="connector" select="';'" />
    <table CtxNum="1" cellspacing="0" class="ms-unselectedtitle" onmouseover="OnMouseOverAdHocFilter(this, '{concat($displayname,$separator,$fieldname, $separator,$fieldtype, $connector, 1033, $separator, $dvt_partguid)}')">
    <tr><td width="100%" class="ms-vb" nowrap="nowrap">
    <a>
    <xsl:attribute name="href">javascript: <xsl:value-of select="ddwrt:GenFireServerEvent(concat('dvt_sortfield={',$sortfield,'};dvt_sortdir={',$sortText,'}'))" />;</xsl:attribute>
    <xsl:attribute name="onclick">javascript: <xsl:value-of select="ddwrt:GenFireServerEvent(concat('dvt_sortfield={',$sortfield,'};dvt_sortdir={',$sortText,'}'))" />;</xsl:attribute>
    <xsl:choose>
    <xsl:when test="$fieldtype = 'Attachments'">
    <xsl:value-of select="$fieldtitle" disable-output-escaping="yes" />
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="$fieldtitle" />
    </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="$dvt_sortfield = $sortfield">
    <xsl:choose>
    <xsl:when test="$dvt_sortdir = 'ascending'">
    <img border="0" alt="Ascending" src="{ddwrt:FieldSortImageUrl('Desc')}" /></xsl:when>
    <xsl:when test="$dvt_sortdir = 'descending'">
    <img border="0" alt="Descending" src="{ddwrt:FieldSortImageUrl('Asc')}" /></xsl:when>
    </xsl:choose>
    </xsl:if>
    </a>
    <xsl:if test="contains($dvt_filterfields, concat($fieldname, ';' ))">
    <IMG SRC="/_layouts/images/filter.gif" BORDER="0" ALT="" />
    </xsl:if>
    </td>
    <td><img src="/_layouts/images/blank.gif" width="13" style="visibility: hidden" alt="" /></td>
    </tr>
    </table>

    </xsl:when>
    <xsl:otherwise>
    <xsl:choose>
    <xsl:when test="$fieldtype = 'Attachments'">
    <xsl:value-of select="$fieldtitle" disable-output-escaping="yes" />
    </xsl:when>
    <xsl:otherwise>
    <xsl:value-of select="$fieldtitle" />
    </xsl:otherwise>
    </xsl:choose>
    </xsl:otherwise>
    </xsl:choose>
    <xsl:if test="$dvt_filterfield=$fieldname" ddwrt:cf_ignore="1"><img alt="Filter" src="{ddwrt:FieldFilterImageUrl('')}" /></xsl:if>
    </xsl:template>
    </xsl:stylesheet>

    5. The first part of the custom XSL is to copy the table that houses the guts of the DFWP. This is our building block for the project. It contains the table, table rows, table headings and the call to the dvt_1.body template.

    The quickest way I can find the housing table is to put your cursor inside of the first table heading cell. In code view you should see an opening tag for the table. Place your cursor in front of the <table and press Control + Shift + Semicolon. This should select the whole table from opening to closing tag. Paste it below the closing tag of the first table.

    Here is a sample of what you're copying:

    <table border="0" width="100%" cellpadding="2" cellspacing="0">
    <tr valign="top">
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <th class="ms-vh" width="1%" nowrap="nowrap"></th>
    </xsl:if>
    <th class="ms-vh" nowrap="nowrap">Title</th>
    <th class="ms-vh" nowrap="nowrap">Created By</th>
    </tr>
    <xsl:call-template name="dvt_1.body">
    <xsl:with-param name="Rows" select="$Rows"/>
    </xsl:call-template>
    </table>

    I'm going to do a demo based on a 800 pixel wide DFWP. I mention this now so that you can go into the table element and change the width from 100% to 300px.

    Make the second table 500px. Adjust this to fit your needs.

    Once this is done you will have two tables on your page. The top one will be 300 px and the bottom one will be 500px.

    6. Place a <div> tag around each table. Give the top table a div id of Fixed and the bottom one Scroll. We'll use these later to create our CSS.

    <div id="Fixed"><table>Bunch of stuff</table></div>

    <div id="Scroll"><table>Bunch of stuff</table></div>

    7. Finally, create another div tag that houses both of the divs we just created and their content. Give it an id of container.

    <div id="Container">
    <div id="Fixed"><table>Bunch of stuff</table></div>

    <div id="Scroll"><table>Bunch of stuff</table></div>
    </div>

    8. Up to this point the view should look the same. Only big difference you might notice is the quick tag selector has extra divs inside of it.

    Place your cursor in the table heading in the bottom data view. This should bring you to the second table that is 500px wide, per this example. Find the following chunk of code:

    <xsl:call-template name="dvt_1.body">
    <xsl:with-param name="Rows" select="$Rows"/>
    </xsl:call-template>

    Change the template name to dvtScroll.body. In code view perform a search for dvt_1.body. This should help you find the actual template we're calling. We need to make a copy of this template and paste it below the existing one. Here is what you should be copying:

    <xsl:template name="dvt_1.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:call-template name="dvt_1.rowview"/>
    </xsl:for-each>
    </xsl:template>

    We'll need to change the templates name we just copied to match the second tables name. So the finished product should look like this:

    <xsl:template name="dvtScroll.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:call-template name="dvt_1.rowview"/>
    </xsl:for-each>
    </xsl:template>


    9. Next, we need to make a copy of the dvt_1.rowview template. In our recently renamed dvtScroll.Body template control click the dvt_1.roview. Use the Control + Shift + Semicolon trick to select the dvt_1.rowview template and paste the whole template below the existing one. This is what the code should look like:

    <xsl:template name="dvt_1.rowview">
    <tr>
    <xsl:if test="position() mod 2 = 1">
    <xsl:attribute name="class">ms-alternating</xsl:attribute>
    </xsl:if>
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <td class="ms-vb" width="1%" nowrap="nowrap">
    <span ddwrt:amkeyfield="ID" ddwrt:amkeyvalue="ddwrt:EscapeDelims(string(@ID))" ddwrt:ammode="view"></span>
    </td>
    </xsl:if>
    <td class="ms-vb">
    <xsl:value-of select="@Title"/>
    </td>
    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>
    </tr>
    </xsl:template>

    The finished product will look like this:

    <xsl:template name="dvtScroll.rowview">
    <tr>
    <xsl:if test="position() mod 2 = 1">
    <xsl:attribute name="class">ms-alternating</xsl:attribute>
    </xsl:if>
    <xsl:if test="$dvt_1_automode = '1'" ddwrt:cf_ignore="1">
    <td class="ms-vb" width="1%" nowrap="nowrap">
    <span ddwrt:amkeyfield="ID" ddwrt:amkeyvalue="ddwrt:EscapeDelims(string(@ID))" ddwrt:ammode="view"></span>
    </td>
    </xsl:if>
    <td class="ms-vb">
    <xsl:value-of select="@Title"/>
    </td>
    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>
    </tr>
    </xsl:template>

    Don't forget to update the dvtScroll.Body template to call the new dvtScroll.rowview. Here is the finished product:

    <xsl:template name="dvtScroll.body">
    <xsl:param name="Rows"/>
    <xsl:for-each select="$Rows">
    <xsl:call-template name="dvtScroll.rowview"/>
    </xsl:for-each>
    </xsl:template>

    10. Now, it's a matter of adding table headings and removing table headings in the two distinct tables. Since the overall project is still contained in the dvt_1 template we should have the same rows variable and in turn the same content between the two web parts.

    For this simplistic data view with only two columns it should be easy to clean up.

    Start by placing your cursor in the top data view and remove the created by table heading. If you have filtering enabled the markup will be slightly more complex but the same principal applies.

    Also, I would recommend having a separate data view if you need to add the table headings with filtering. The extra data view is just there to have SPD generate the <th> markup so you don't have to do it by hand. If you know how to do it by hand more power to you.

    <th class="ms-vh" nowrap="nowrap">Created By</th>

    11. Next, go into the dvt_1.rowview template and remove the <td> that contains the Created by column. It's going to be @Author.

    <td class="ms-vb">
    <xsl:value-of select="@Author" disable-output-escaping="yes"/>
    </td>

    Note:
    ================
    The only thing that gets these out of alignment is columns that span more than one row. Rich text columns are the main one that gave me some guff. I opted to substring the value so it would only display 20 characters and then ... I made it a hyperlink so if somebody wanted to view the full content it would pass a URL query string to the current page and a display form would show the information. This is totally an aside though and probably could be done with modal windows or any other HTML magic you feel like implementing.
    ================

    12. Finally, remove the extra columns in the second table. The second table should only have the table heading for created by and the table data for created by.

    To summarize, the first table will have the Title heading and Title column data and the second table will house Created by.

    13. We're ready to apply some CSS to make this look like one data view. This is what I came up with. Place this on the page somewhere or reference it via a CSS file. The CSS seems to work on IE, FF and Safari.

    <style type="text/css">
    #Container{width:800px;position:relative;padding:0px;margin:0px;border:0px;}
    #Fixed{width:300px;position:relative;padding:0px;margin:0px;border:0px;}
    #Scroll{width:500px;position:absolute;top:0px;left:300px;padding:0px;margin:0px;border:0px;overflow-x:scroll;}
    </style>

    ------------------

    Regards,

    Jing Wang | Microsoft Online Community Support

     

    • Marked as answer by Daniel Keith Thursday, January 5, 2012 5:33 PM
    Thursday, January 5, 2012 5:27 PM