142

How do you shade alternating rows in a SQL Server Reporting Services report?


Edit: There are a bunch of good answers listed below--from quick and simple to complex and comprehensive. Alas, I can choose only one...

Community
  • 1
  • 1
Michael Haren
  • 97,268
  • 39
  • 159
  • 200

18 Answers18

213

Go to the table row's BackgroundColor property and choose "Expression..."

Use this expression:

= IIf(RowNumber(Nothing) Mod 2 = 0, "Silver", "Transparent")

This trick can be applied to many areas of the report.

And in .NET 3.5+ You could use:

= If(RowNumber(Nothing) Mod 2 = 0, "Silver", "Transparent")

Not looking for rep--I just researched this question myself and thought I'd share.

Shimmy Weitzhandler
  • 92,920
  • 119
  • 388
  • 596
Michael Haren
  • 97,268
  • 39
  • 159
  • 200
  • 10
    This fails under certain circumstances, particularly in tables and matrix objects with lots of sub-totals. Catch22's response does not have the same limitations. Also, Catch22's method can be used to force columns in a matrix to have alternating column colors which is useful once in a blue moon. – Registered User Jan 01 '09 at 17:53
  • 16
    I always make sure to upvote someone who answers their own question. Too often, the asker finds an answer on their own and then never comes back to post it, leaving the rest of us who might have the same question in the dark. A bump for you, sir. – Matt DiTrolio Aug 31 '11 at 14:43
  • 4
    When I use the above code I get a warning message like `[rsInvalidColor] The value of the BackgroundColor property for the textbox ‘active’ is “Transparent”, which is not a valid BackgroundColor.` It looks like the correct expression would be `=IIf(RowNumber(Nothing) Mod 2 = 0, "Silver", Nothing)`. Thanks for the tip, though. – Russell B Aug 27 '13 at 19:10
  • 2
    This gives the problem in case of grouping and details view –  Sep 06 '13 at 05:03
  • 3
    some browsers cant handle "Transparent" or "Nothing". Best bet would be to use "White" – Nate S. Sep 16 '14 at 18:05
89

Using IIF(RowNumber...) can lead to some issues when rows are being grouped and another alternative is to use a simple VBScript function to determine the color.

It's a little more effort but when the basic solution does not suffice, it's a nice alternative.

Basically, you add code to the Report as follows...

Private bOddRow As Boolean
'*************************************************************************
' -- Display green-bar type color banding in detail rows
' -- Call from BackGroundColor property of all detail row textboxes
' -- Set Toggle True for first item, False for others.
'*************************************************************************
Function AlternateColor(ByVal OddColor As String, _
         ByVal EvenColor As String, ByVal Toggle As Boolean) As String
    If Toggle Then bOddRow = Not bOddRow
    If bOddRow Then
        Return OddColor
    Else
        Return EvenColor
    End If
End Function

Then on each cell, set the BackgroundColor as follows:

=Code.AlternateColor("AliceBlue", "White", True)

Full details are on this Wrox article

Catch22
  • 2,991
  • 26
  • 33
  • The above code is either added to the Code section of the report or to a code-behind page in a VB.NET project, compiled, and deployed as a DLL that is reference as an assembly. I recommend making the extra effort to deploying this as a dll since you generally reference this in numerous reports. – Registered User Jan 01 '09 at 17:58
  • This is my preferred way for handling the grouping problem, after trying a few other hacks. It doesn't break down when interactive sort is applied to a column. +1 and many thanks. – Rex Miller Jan 27 '09 at 03:07
  • The colors are offset when you have an odd number of rows. – K Richard Jun 14 '10 at 17:41
  • 20
    Don't forget to change "True" to "False" for all columns in the row after the first, otherwise you will see a checkerboard effect! Thanks for the solution - it works great! – Peter Mularien Mar 13 '11 at 05:13
  • Peter Mularien: I am having this problem, can you explain how to fix it? – Bill Software Engineer Dec 22 '12 at 17:19
  • You should select each cell in turn on the row and look at the BackgroundColor property. Where you see Code.AlternateColor("AliceBlue", "White", xxxx), change xxxx to either True or False to switch the colors. – Catch22 Dec 22 '12 at 21:28
  • Thank you! After lots of struggles with RunningValue (and weirdness like colors applied before custom sorting happened), this was a much easier approach. I added another function to reset it to true and called that from a function that got the value of the outer group label. This way each grouping would start with the same color (made sense in my case since there was a lot of space between the groups). – ShawnFumo Jun 07 '16 at 21:48
68

I got the chess effect when I used Catch22's solution, I think because my matrix has more than one column in design. that expression worked fine for me :

=iif(RunningValue(Fields![rowgroupfield].Value.ToString,CountDistinct,Nothing) Mod 2,"Gainsboro", "White")
ahmad
  • 681
  • 5
  • 2
  • 14
    This answer deserves much more attention - it's a clean solution that works perfectly in a matrix with row groups and columns groups and requires no custom code to work. Beautiful! – Stefan Mohr Mar 09 '12 at 23:19
  • 9
    Actually, this works fine even for multiple columns - but not if you have "missing" data in a grouping. So if you have data that has 12 months of data data in 2007 but no January data in 2006, and group by month on rows and year on columns, the 2006 coloring will be off by one because the RunningValue gets offsync because even though there is still a box in the matrix for "January 2006" there's no data in the dataset and the RunningValue remains the same, no color change, etc. – Kyle Hale Dec 03 '12 at 20:18
  • 2
    @ahmad this is so close. I'm having the issue where empty boxes use the opposite color. how do i make sure that empty boxes get colored properly? – FistOfFury Dec 02 '13 at 18:32
  • @KyleHale do you have a fix for the missing box coloring? – FistOfFury Dec 02 '13 at 18:35
  • 2
    @FistOfFury The only solution I know is to ensure a value is set (usually 0) for missing data. – Kyle Hale Dec 02 '13 at 19:27
  • When the accepted answer didn't alternate properly for me, I went straight to this one, rather than faffing around with VBA and different functions per row like some other answers. At least for my data, this answer works perfectly. Thanks! – underscore_d Nov 23 '15 at 13:41
20

I have changed @Catch22's solution A bit as I do not like the idea of having to go into each field if I decide I want to change one of the colors. This is especially important in reports where the are numerous fields that would need to have the color variable changed.

'*************************************************************************
' -- Display alternate color banding (defined below) in detail rows
' -- Call from BackgroundColor property of all detail row textboxes
'*************************************************************************
Function AlternateColor(Byval rowNumber as integer) As String
    Dim OddColor As String = "Green"
    Dim EvenColor As String = "White"

    If rowNumber mod 2 = 0 then 
        Return EvenColor
    Else
        Return OddColor
    End If
End Function

Noticed that I have change the function from one that accepts the colors to one that contains the colors to be used.

Then in each field add:

=Code.AlternateColor(rownumber(nothing))

This is much more robust than manually changing the color in each fields' background color.

Noppadet
  • 805
  • 8
  • 19
Michael Eakins
  • 4,121
  • 3
  • 31
  • 54
  • 1
    Nice addition to this thread! I like not having to set "True" or "False" in each column. I also like the option of "mod 3" so I can shade only every 3rd row. – Baodad Nov 25 '13 at 21:09
  • I also like this solution. However, it does not work for me when I use row grouping. @ahmad's [solution](http://stackoverflow.com/a/6777983/450750) above worked for me though. – Baodad Mar 22 '16 at 21:17
  • This is a great solution for a tablix if you do not wish to add additional fields to your DataSet! – clamchoda Sep 08 '16 at 15:57
16

One thing I noticed is that neither of the top two methods have any notion of what color the first row should be in a group; the group will just start with the opposite color from the last line of the previous group. I wanted my groups to always start with the same color...the first row of each group should always be white, and the next row colored.

The basic concept was to reset the toggle when each group starts, so I added a bit of code:

Private bOddRow As Boolean
'*************************************************************************
' -- Display green-bar type color banding in detail rows
' -- Call from BackGroundColor property of all detail row textboxes
' -- Set Toggle True for first item, False for others.
'*************************************************************************
Function AlternateColor(ByVal OddColor As String, _
         ByVal EvenColor As String, ByVal Toggle As Boolean) As String
    If Toggle Then bOddRow = Not bOddRow
    If bOddRow Then
        Return OddColor
    Else
        Return EvenColor
    End If
End Function
'
Function RestartColor(ByVal OddColor As String) As String
    bOddRow = True
    Return OddColor
End Function

So I have three different kinds of cell backgrounds now:

  1. First column of data row has =Code.AlternateColor("AliceBlue", "White", True) (This is the same as the previous answer.)
  2. Remaining columns of data row have =Code.AlternateColor("AliceBlue", "White", False) (This, also, is the same as the previous answer.)
  3. First column of grouping row has =Code.RestartColor("AliceBlue") (This is new.)
  4. Remaining columns of grouping row have =Code.AlternateColor("AliceBlue", "White", False) (This was used before, but no mention of it for grouping row.)

This works for me. If you want the grouping row to be non-colored, or a different color, it should be fairly obvious from this how to change it around.

Please feel free to add comments about what could be done to improve this code: I'm brand new to both SSRS and VB, so I strongly suspect that there's plenty of room for improvement, but the basic idea seems sound (and it was useful for me) so I wanted to throw it out here.

Beska
  • 11,976
  • 14
  • 73
  • 108
  • You can also reset the row numbering for each group as described in [this answer](https://stackoverflow.com/a/1478089/8773089). – StackOverthrow Aug 21 '18 at 21:44
10

for group headers/footers:

=iif(RunningValue(*group on field*,CountDistinct,"*parent group name*") Mod 2,"White","AliceBlue")

You can also use this to “reset” the row color count within each group. I wanted the first detail row in each sub group to start with White and this solution (when used on the detail row) allowed that to happen:

=IIF(RunningValue(Fields![Name].Value, CountDistinct, "NameOfPartnetGroup") Mod 2, "White", "Wheat")

See: http://msdn.microsoft.com/en-us/library/ms159136(v=sql.100).aspx

John Saunders
  • 157,405
  • 24
  • 229
  • 388
8

Michael Haren's solution works fine for me. However i got a warning saying that "Transparent" is not a valid BackgroundColor when Preview. Found a quick fix from Setting BackgroundColor of Report elements in SSRS. Use Nothing instead of "Transparent"

= IIf(RowNumber(Nothing) Mod 2 = 0, "Silver", Nothing)
nonetaku
  • 293
  • 1
  • 5
  • 7
6

The only effective way to solve this without using VB is to "store" the row grouping modulo value within the row grouping (and outside the column grouping) and reference it explicitly within your column grouping. I found this solution at

http://ankeet1.blogspot.com/2009/02/alternating-row-background-color-for.html

But Ankeet doesn't the best job of explaining what's happening, and his solution recommends the unnecessary step of creating a grouping on a constant value, so here's my step-by-step process for a matrix with a single row group RowGroup1:

  1. Create a new column within the RowGroup1. Rename the textbox for this to something like RowGroupColor.
  2. Set the Value of RowGroupColor's textbox to

    =iif(RunningValue(Fields![RowGroupField].Value ,CountDistinct,Nothing) Mod 2, "LightSteelBlue", "White")

  3. Set the BackgroundColor property of all your row cells to

    "=ReportItems!RowGroupColor.Value"

  4. Set the width of the the RowGroupColor column to 0pt and set CanGrow to false to hide it from clients.

Voila! This also solves a lot of the problems mentioned in this thread:

  • Automatic resets for subgroups: Just add a new column for that rowgroup, performing a RunningValue on its group values.
  • No need to worry about True/False toggles.
  • Colors only held in one place for easy modification.
  • Can be used interchangeably on row or column groups (just set height to 0 instead of width)

It would be awesome if SSRS would expose properties besides Value on Textboxes. You could just stuff this sort of calculation in a BackgroundColor property of the row group textboxes and then reference it as ReportItems!RowGroup.BackgroundColor in all of the other cells.

Ahh well, we can dream ...

Kyle Hale
  • 7,447
  • 1
  • 32
  • 56
5

My problem was that I wanted all the columns in a row to have the same background. I grouped both by row and by column, and with the top two solutions here I got all the rows in column 1 with a colored background, all the rows in column 2 with a white background, all the rows in column 3 with a colored background, and so on. It's as if RowNumber and bOddRow (of Catch22's solution) pay attention to my column group instead of ignoring that and only alternating with a new row.

What I wanted is for all the columns in row 1 to have a white background, then all the columns in row 2 to have a colored background, then all the columns in row 3 to have a white background, and so on. I got this effect by using the selected answer but instead of passing Nothing to RowNumber, I passed the name of my column group, e.g.

=IIf(RowNumber("MyColumnGroupName") Mod 2 = 0, "AliceBlue", "Transparent")

Thought this might be useful to someone else.

Sarah Vessels
  • 27,994
  • 29
  • 147
  • 217
4

I think this trick is not discussed here. So here it is,

In any type of complex matrix, when you want alternate cell colors, either row wise or column wise, the working solution is,

If you want a alternate color of cells coloumn wise then,

  1. At the bottom right corner of a report design view, in "Column Groups", create a fake parent group on 1 (using expression), named "FakeParentGroup".
  2. Then, in the report design, for cells that to be colored alternatively, use following background color expression

=IIF(RunningValue( Fields![ColumnGroupField].Value, countDistinct, "FakeParentGroup" ) MOD 2, "White", "LightGrey")

Thats all.

Same for the alternate color row wise, just you have to edit solution accordingly.

NOTE: Here, sometimes you need to set border of cells accordingly, usually it vanishes.

Also dont forget to delete value 1 in report that came into pic when you created fake parent group.

Aditya
  • 2,103
  • 5
  • 23
  • 45
  • 1
    This answer is definitely a great find for those who have to create row banding in complex tablixes! If there is an existing parent group, use it. Otherwise, create FakeParentGroup. Missing values in tablixes that have both column and row groups usually throw off the formatting. See my answer that builds on @Aditya's to account for missing values in the first cell. – rpyzh May 20 '14 at 19:29
2

@Aditya's answer is great, but there are instances where formatting will be thrown off if the very first cell of the row (for row background formatting) has a missing value (in complex tablixes with column/rows groups and missing values).

@Aditya's solution cleverly leverages countDistinct result of runningValue function to identify row numbers within a tablix (row) group. If you have tablix rows with missing value in the first cell, runningValue will not increment countDistinct result and it will return the previous row's number (and, therefore, will affect the formatting of that cell). To account for that, you will have to add an additional term to offset the countDistinct value. My take was to check the first running value in the row group itself (see line 3 of the snippet below):

=iif(
    (RunningValue(Fields![RowGroupField].Value, countDistinct, "FakeOrRealImmediateParentGroup")
    + iif(IsNothing(RunningValue(Fields![RowGroupField].Value, First, "GroupForRowGroupField")), 1, 0)
    ) mod 2, "White", "LightGrey")

Hope this helps.

rpyzh
  • 797
  • 5
  • 9
  • Thanks @rpyzh, this was the solution that worked for my multi-row group with a common parent row group Report! – Niallty Nov 06 '18 at 18:23
2

If for the entire report you need an alternating color, you can use the DataSet your Tablix is bound to for a report-wide identity rownumber on the report and use that in the RowNumber function...

=IIf(RowNumber("DataSet1")  Mod 2 = 1, "White","Blue")
John Saunders
  • 157,405
  • 24
  • 229
  • 388
Matt
  • 21
  • 1
1

I tried all these solutions on a Grouped Tablix with row spaces and none worked across the entire report. The result was duplicate colored rows and other solutions resulted in alternating columns!

Here is the function I wrote that worked for me using a Column Count:

Private bOddRow As Boolean
Private cellCount as Integer

Function AlternateColorByColumnCount(ByVal OddColor As String, ByVal EvenColor As String, ByVal ColCount As Integer) As String

if cellCount = ColCount Then 
bOddRow = Not bOddRow
cellCount = 0
End if 

cellCount  = cellCount  + 1

if bOddRow Then
 Return OddColor
Else
 Return EvenColor
End If

End Function

For a 7 Column Tablix I use this expression for Row (of Cells) Backcolour:

=Code.AlternateColorByColumnCount("LightGrey","White", 7)
Jeremy Thompson
  • 52,213
  • 20
  • 153
  • 256
1

Could someone explain the logic behind turning rest of the fields to false in below code (from above post)

One thing I noticed is that neither of the top two methods have any notion of what color the first row should be in a group; the group will just start with the opposite color from the last line of the previous group. I wanted my groups to always start with the same color...the first row of each group should always be white, and the next row colored.

The basic concept was to reset the toggle when each group starts, so I added a bit of code:

Private bOddRow As Boolean
'*************************************************************************
'-- Display green-bar type color banding in detail rows
'-- Call from BackGroundColor property of all detail row textboxes
'-- Set Toggle True for first item, False for others.
'*************************************************************************
'
Function AlternateColor(ByVal OddColor As String, _
                  ByVal EvenColor As String, ByVal Toggle As Boolean) As String
         If Toggle Then bOddRow = Not bOddRow
         If bOddRow Then 
                Return OddColor
         Else
                 Return EvenColor
         End If
 End Function
 '
 Function RestartColor(ByVal OddColor As String) As String
         bOddRow = True
         Return OddColor
 End Function

So I have three different kinds of cell backgrounds now:

  1. First column of data row has =Code.AlternateColor("AliceBlue", "White", True) (This is the same as the previous answer.)
  2. Remaining columns of data row have =Code.AlternateColor("AliceBlue", "White", False) (This, also, is the same as the previous answer.)
  3. First column of grouping row has =Code.RestartColor("AliceBlue") (This is new.)
  4. Remaining columns of grouping row have =Code.AlternateColor("AliceBlue", "White", False) (This was used before, but no mention of it for grouping row.)

This works for me. If you want the grouping row to be non-colored, or a different color, it should be fairly obvious from this how to change it around.

Please feel free to add comments about what could be done to improve this code: I'm brand new to both SSRS and VB, so I strongly suspect that there's plenty of room for improvement, but the basic idea seems sound (and it was useful for me) so I wanted to throw it out here.

Jamie F
  • 21,567
  • 4
  • 55
  • 73
misha
  • 19
  • 1
0

Just because none of the answers above seemed to work in my matrix, I'm posting this here:

http://reportingservicestnt.blogspot.com/2011/09/alternate-colors-in-matrixpivot-table.html

CodeRedick
  • 7,062
  • 7
  • 43
  • 70
0

My matrix data had missing values in it, so I wasn't able to get ahmad's solution to work, but this solution worked for me

Basic idea is to create a child group and field on your innermost group containing the color. Then set the color for each cell in the row based on that field's value.

FistOfFury
  • 5,598
  • 5
  • 43
  • 55
  • Check out @Aditya's and my answer to handle missing values in the matrix. Useful if you already have (or willing to create a fake) parent group. You won't have to create a separate field that way. – rpyzh May 21 '14 at 15:27
0

Slight modification of other answers from here that worked for me. My group has two values to group on, so I was able to just put them both in the first arg with a + to get it to alternate correctly

= Iif ( RunningValue (Fields!description.Value + Fields!name.Value, CountDistinct, Nothing) Mod 2 = 0,"#e6eed5", "Transparent")
0

When using row and column groups both, I had an issue where the colors would alternate between the columns even though it was the same row. I resolved this by using a global variable that alternates only when the row changes:

Public Dim BGColor As String = "#ffffff"

Function AlternateColor() As String
  If BGColor = "#cccccc" Then
    BGColor = "#ffffff"
    Return "#cccccc"
  Else
    BGColor = "#cccccc"
    Return "#ffffff"
  End  If
End Function

Now, in the first column of the row you want to alternate, set the color expression to:

=Code.AlternateColor()

-

In the remaining columns, set them all to:

=Code.BGColor

This should make the colors alternate only after the first column is drawn.

This may (unverifiably) improve performance, too, since it does not need to do a math computation for each column.

Eneerge
  • 61
  • 6