Second Life of a Hungarian SharePoint Geek

March 17, 2011

Displaying results in multiple columns using the Content Query Web Part – the other direction

Filed under: CQWP, SP 2010 — Tags: , — Peter Holpar @ 09:37

I posted a solution for something similar more than a year ago, but a commenter asked me if it is possible (and how) to do the same in the opposite direction.

For example, assuming eight articles and a two-column display, in the original post the articles were ordered like this:

1 2
3 4
5 6
7 8

Instead of this, we should now have the following result:

1 5
2 6
3 7
4 8

Let’s see, what makes it a bit harder than the first approach. In HTML, the tables are build row-by-row, that is the first row is populated first with cells, then starts the second row, etc. until there is no more item and rendering finished at the last cell. That is very practical and easy to implement in the case of a Content by Query Web Part since it processes items in the order returned by the query.

(Side note: if it was not HTML, but WPF / Silverlight we could position items into arbitrary grid cell, that would make things much more easier. I’m not yet very familiar with HTML5, but I assume it should provide more flexibility for this kind of positioning as well. Comments are welcome to confirm or refute this assumption.)

Putting items in the same row into the same table row either has the definite advantage that it guarantees the height of the cells will be identical. When processing the items column-by-column you lose this possibility. You have to separate items some other way (for example breaks instead of table rows) that makes height synchronization rather hard. Either you should have items with the same original height or wrap your items into a container (like a div) that guarantees the identical height through its properties.

In this solution there is no difference in preparation of the ContentQueryMain.xsl file related to the original version. Please, see the description of the changes there.

The customization of the ItemStyle.xsl required a bit more speculation than expected, but after all that was not a rocket science either.

First, we set the column number as an XSLT variable again:

<xsl:variable name="ColNum" select="3" />

Columns may contain the same number of the items (rows) or there might be columns that contain more items than the others depending whether the overall item number can be divided by the column number without remainder. In this latter case these columns are the first ones (from the left), and they contains exactly one extra item. The number of columns with this extra item equals the modulo of the above-mentioned division.

Turning this theory into code results the following:

  1. <xsl:variable name="ModNum" select="$LastRow mod $ColNum" />
  2. <xsl:variable name="RowNumBase" select="($LastRow – $ModNum) div $ColNum" />
  4. <xsl:variable name="RowNum">
  5.     <xsl:choose>
  6.         <xsl:when test="$CurPos &lt;= ($RowNumBase + 1) * $ModNum">
  7.             <xsl:value-of select="$RowNumBase + 1" />
  8.         </xsl:when>
  9.         <xsl:otherwise>
  10.             <xsl:value-of select="$RowNumBase" />
  11.         </xsl:otherwise>
  12.     </xsl:choose>
  13. </xsl:variable>

To be able to find the correct column endings, after processing the columns with the extra items we have to update the value of the current position counter to remove the extra items from the count:

  1. <xsl:variable name="CurPosUpd">
  2.     <xsl:choose>
  3.         <xsl:when test="$CurPos &lt;= ($RowNumBase + 1) * $ModNum">
  4.             <xsl:value-of select="$CurPos" />
  5.         </xsl:when>
  6.         <xsl:otherwise>
  7.             <xsl:value-of select="$CurPos – $ModNum" />
  8.         </xsl:otherwise>
  9.     </xsl:choose>
  10. </xsl:variable>

There are special formatting instructions before the first item,

  1. <xsl:if test="$CurPos = 1">
  2.     <xsl:text disable-output-escaping="yes">&lt;div&gt;&lt;table&gt;&lt;tr&gt;&lt;td width=&quot;</xsl:text><xsl:value-of select="round(100 div $ColNum)" /><xsl:text disable-output-escaping="yes">%&quot; valign=&quot;top&quot;&gt;</xsl:text>
  3. </xsl:if>

after the last item,

  1. <xsl:if test="$CurPos = $LastRow">
  2.     <xsl:text disable-output-escaping="yes">&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</xsl:text>
  3. </xsl:if>

and after the last items of each columns (except the overall last item):

  1. <xsl:if test="($CurPosUpd mod $RowNum = 0) and ($CurPos != $LastRow)">
  2.     <xsl:text disable-output-escaping="yes">&lt;/td&gt;&lt;td width=&quot;</xsl:text><xsl:value-of select="round(100 div $ColNum)" /><xsl:text disable-output-escaping="yes">%&quot; valign=&quot;top&quot;&gt;</xsl:text>
  3. </xsl:if>

Here is the complete XSLT source of our new News template:

  1. <xsl:template name="News" match="Row[@Style='News']" mode="itemstyle">
  2. <xsl:param name="CurPos" />
  3. <xsl:param name="LastRow" />
  4. <xsl:variable name="SafeLinkUrl">
  5.    <xsl:call-template name="OuterTemplate.GetSafeLink">
  6.      <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
  7.    </xsl:call-template>
  8. </xsl:variable>
  9. <xsl:variable name="SafeImageUrl">
  10.     <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
  11.       <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
  12.     </xsl:call-template>
  13.   </xsl:variable>
  14.   <xsl:variable name="DisplayTitle">
  15.     <xsl:call-template name="OuterTemplate.GetTitle">
  16.       <xsl:with-param name="Title" select="@Title"/>
  17.       <xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
  18.     </xsl:call-template>
  19.   </xsl:variable>
  20.   <xsl:variable name="LinkTarget">
  21.     <xsl:if test="@OpenInNewWindow = 'True'" >_blank</xsl:if>
  22.   </xsl:variable>
  24.   <xsl:variable name="ColNum" select="3" />
  25.   <xsl:variable name="ModNum" select="$LastRow mod $ColNum" />
  26.   <xsl:variable name="RowNumBase" select="($LastRow – $ModNum) div $ColNum" />
  28.   <xsl:variable name="RowNum">
  29.     <xsl:choose>
  30.       <xsl:when test="$CurPos &lt;= ($RowNumBase + 1) * $ModNum">
  31.           <xsl:value-of select="$RowNumBase + 1" />
  32.       </xsl:when>
  33.       <xsl:otherwise>
  34.         <xsl:value-of select="$RowNumBase" />
  35.       </xsl:otherwise>
  36.     </xsl:choose>
  37.   </xsl:variable>
  39.   <xsl:variable name="CurPosUpd">
  40.     <xsl:choose>
  41.       <xsl:when test="$CurPos &lt;= ($RowNumBase + 1) * $ModNum">
  42.           <xsl:value-of select="$CurPos" />
  43.       </xsl:when>
  44.       <xsl:otherwise>
  45.         <xsl:value-of select="$CurPos – $ModNum" />
  46.       </xsl:otherwise>
  47.     </xsl:choose>
  48.   </xsl:variable>
  51.   <xsl:if test="$CurPos = 1">
  52.     <xsl:text disable-output-escaping="yes">&lt;div&gt;&lt;table&gt;&lt;tr&gt;&lt;td width=&quot;</xsl:text><xsl:value-of select="round(100 div $ColNum)" /><xsl:text disable-output-escaping="yes">%&quot; valign=&quot;top&quot;&gt;</xsl:text>
  53.   </xsl:if>
  54.     <table width="90%">
  55.       <tr height="27px" valign="top">
  56.         <td>
  57.           <span>
  58.             <a href="{$SafeLinkUrl}">
  59.               <xsl:value-of select="@Title"/>
  60.             </a>
  61.           </span>
  62.           <br />
  63.           <span>
  64.              <xsl:value-of select="ddwrt:FormatDateTime(@ArticleStartDate, 1033, 'MM-dd-yyyy')"/>
  65.           </span>
  66.         </td>
  67.       </tr>
  68.       <tr height="35px" min-height="35px" valign="top">
  69.         <td>
  70.           <span>
  71.             <xsl:if test="string-length($SafeImageUrl) != 0">
  72.               <div class="image-area-left">
  73.                 <a href="{$SafeLinkUrl}" target="{$LinkTarget}">
  74.                   <img class="image" width="100px" height="100px" src="{$SafeImageUrl}" alt="{@ImageUrlAltText}" />
  75.                 </a>
  76.               </div>
  77.             </xsl:if>
  78.             <xsl:value-of select="substring(@Comments,0,200)"/>
  79.             <xsl:if test="string-length(@Comments) &gt; 200">…</xsl:if>
  80.           </span>
  81.           <a href="{$SafeLinkUrl}"> Details…</a>
  82.         </td>
  83.       </tr>
  84.     </table>
  86.   <xsl:if test="($CurPosUpd mod $RowNum = 0) and ($CurPos != $LastRow)">
  87.     <xsl:text disable-output-escaping="yes">&lt;/td&gt;&lt;td width=&quot;</xsl:text><xsl:value-of select="round(100 div $ColNum)" /><xsl:text disable-output-escaping="yes">%&quot; valign=&quot;top&quot;&gt;</xsl:text>
  88.   </xsl:if>
  90.   <xsl:if test="$CurPos = $LastRow">
  91.     <xsl:text disable-output-escaping="yes">&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;</xsl:text>
  92.   </xsl:if>
  94.   </xsl:template>

Don’t forget to add the ddwrt namespace to the stylesheet to enable date formatting.


I’ve added Comments and ArticleStartDate fields to the view fields:

<property name="CommonViewFields" type="string">Comments,Note;ArticleStartDate,DateTime</property>

BTW, SharePoint 2010 makes that easier for you, see the settings of CQWP:


I’ve set the items to be ordered by the Article Date field,


and the CQWP to apply the News item style.


Finally, these are the results of the rendering for different column number values specified in the ColNum variable:

For value 1 (that I assume no worth after all of these above):


For value 2:


For value 3:


For value 4:


For value 5:




  1. Thank you for the post. Is there a way to send the fields of data from Left to Right as shown below:

    Currrently displaying this way:

    Would like to show this way:
    Title Description Date Location

    Comment by Jeannine — March 27, 2012 @ 21:04

    • Yes, it should be possible. You have to alter the formatting XSL to match your needs.

      Comment by Peter Holpar — April 9, 2012 @ 20:36

  2. Hi, great job. Is posible change the colnum value from edit webpart panel?

    Comment by Raul — September 20, 2012 @ 11:25

  3. Péter

    Thanks for such a wonderful article.. It helped a lot.. 🙂

    Sandeep Parandekar

    Comment by Sandeep — January 30, 2013 @ 07:11

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

Create a free website or blog at

%d bloggers like this: