Creating an RDLC Table Template - Implementation Questions RRS feed

  • Question

  • Hello,

    I have seen a number of implementations for creating custom Column Headers in tables including parameter based "switches" used to select a pre-defined set of values, and altering the xml directly.

    I am working on a variation where the rdlc accepts a generic object (which we will call a ReportDataContainer).
    I throw any data and formatting info that I would like to have displayed, and viola! (as if it were ever that simple)

    I pass the report a list of ReportDataContainer objects.
    Me.ReportDataSourceContainerBindingSource.DataSource = GenerateReportDataContainerList()

    I will paste the code below, but first an explanation on the object's properties:
    The ReportDataContainer contains the following:
    A list of elements (each element represents a cell's data and formatting entered as an expression in the cell)
    A list of Headers/Strings (we only care about the first ReportDataContainer object in the list)
    A "GroupBy" value (index/Integer - again we only care about the value stored in the first ReportDataContainer object in the list)
    A SortedByIndexArray (basically used to fill the Sorting fields for the table)

    The goal is to create this rdlc once and use it for any number of variations that fit within the scope of the objects stored in my program.   Through several layers of abstraction, I'm hoping to write once, use all over the place.

    rdlc table:
    Header Definitions:

    Group By Definition:
    =Fields!Elements.Value.item( Parameters!GroupByIndex.Value )
    'Notice I'm using a parameter, not the GroupByIndex value in my ReportDataContainer object.  It didn't seem to like
    =Fields!Elements.Value.item( First(Fields!GroupByIndex.Value)) and I have no idea how to debug so I'm leaving this be for now.


    Code Snippet

    Public Class ReportDataContainer
        Private m_Header As New List(Of String)
        Private m_Elements As New List(Of Object)
        Private m_GroupBy As Integer

        Public Sub New()
        End Sub
        Public Sub New(ByVal ElemList As List(Of Object))
            Elements = ElemList
        End Sub

        Public Property Headers() As List(Of String)
                Return m_Header
            End Get
            Set(ByVal value As List(Of String))
                m_Header = value
            End Set
        End Property

        Public Property Elements() As List(Of Object)
                Return m_Elements
            End Get
            Set(ByVal value As List(Of Object))
                m_Elements = value
            End Set
        End Property

        Public Property GroupByIndex() As Integer
                Return m_GroupBy
            End Get
            Set(ByVal value As Integer)
                m_GroupBy = value
            End Set
        End Property

        'Used to determine the sort order via a couple of expressions
        Private m_SortByIndexArray As Integer
        Public Property SortByIndextArray() As Integer
                Return m_SortByIndexArray
            End Get
            Set(ByVal value As Integer)
                m_SortByIndexArray = value
            End Set
        End Property
    End Class

    'I don't even know if I can do objects inside of objects - this is all untested.

    Public Class Element

        Public Sub New()
        End Sub

        Public Sub New(ByVal displayData As Object, Optional ByVal formatCode As String = "")
            Me.DisplayData = displayData
            Me.FormatCode = formatCode
        End Sub

        Private m_DisplayData As Object
        Public Property DisplayData() As Object
                Return m_DisplayData
            End Get
            Set(ByVal value As Object)
                m_DisplayData = value
            End Set
        End Property

    #Region "Format Properties"

        Private m_FormatCode As String = ""
        Public Property FormatCode() As String
                Return m_FormatCode
            End Get
            Set(ByVal value As String)
                m_FormatCode = value
            End Set
        End Property

        Private m_FontFamily As String = "Arial"
        Public Property FontFamily() As String
                Return m_FontFamily
            End Get
            Set(ByVal value As String)
                m_FontFamily = value
            End Set
        End Property

        Private m_size As String = "10pt"
        Public Property Size() As String
                Return m_size
            End Get
            Set(ByVal value As String)
                m_size = value
                If Not value.EndsWith("pt") Then
                    m_size &= "pt"
                End If
            End Set
        End Property
    #End Region
    End Class

    This is my code that generates my test data
    Code Snippet

        Public Function GenerateInputs() As ReportDataContainer()
            Dim lst As New List(Of ReportDataContainer)
            Dim Rnd As New Random

            'Generate the first manually to include RowHeaders, and GroupByIndex
            Dim tempReportDSC As New ReportDataContainer
            tempReportDSC.Headers.Add("Book")       '0
            tempReportDSC.Headers.Add("Asst Name")  '1
            tempReportDSC.Headers.Add("Amount")     '2
            tempReportDSC.Headers.Add("Paid Price") '3
            tempReportDSC.Headers.Add("Mkt Price")  '4
            tempReportDSC.Headers.Add("PNL")        '5
            tempReportDSC.Headers.Add("UPNL")       '6
            tempReportDSC.Headers.Add("Div/Int")    '7

            tempReportDSC.Elements.Add("bFoo") 'New Element("bFoo")) '0
            tempReportDSC.Elements.Add("firstAsset") 'New Element("firstAsset")) '1
            tempReportDSC.Elements.Add(100) 'New Element(100)) '2
            tempReportDSC.Elements.Add(5.5) 'New Element(5.5)) '3
            tempReportDSC.Elements.Add(6.0) 'New Element(6.0)) '4
            tempReportDSC.Elements.Add(50) 'New Element(50)) '5
            tempReportDSC.Elements.Add(10) 'New Element(10)) '6
            tempReportDSC.Elements.Add(0) 'New Element(0)) '7

            tempReportDSC.GroupByIndex = 0
            tempReportDSC.SortByIndextArray = 2


            For i As Integer = 0 To 1
                tempReportDSC = New ReportDataContainer

                If i Mod 2 = 0 Then
                    tempReportDSC.Elements.Add("bFoo") 'New Element("bFoo")) '0
                    tempReportDSC.Elements.Add("bBar") 'New Element("bBar")) '0
                End If

                tempReportDSC.Elements.Add("AssetNumber" & (i + 1).ToString) 'New Element("AssetNumber" & (i + 1).ToString)) '1
                tempReportDSC.Elements.Add(Rnd.Next) 'New Element(Rnd.Next)) '2
                tempReportDSC.Elements.Add(Rnd.Next / 1000) 'New Element(Rnd.Next / 1000)) '3
                tempReportDSC.Elements.Add(Rnd.Next / 1000) 'New Element(Rnd.Next / 1000)) '4
                tempReportDSC.Elements.Add(Rnd.Next / 1000) 'New Element(Rnd.Next / 1000)) '5
                tempReportDSC.Elements.Add(Rnd.Next / 1000) 'New Element(Rnd.Next / 1000)) '6
                tempReportDSC.Elements.Add(Rnd.Next / 1000) 'New Element(Rnd.Next / 10000)) '7


            Return lst.ToArray
        End Function

    Problems:  My simplest test case works so far, but I'm having trouble assigning values to the rdlc.

    What works:
    Grouping works (with the caveat noted above in my description of the ReportDataContainer), I can display the multiple rows and multiple groups in the table body.

    What Doesn't Work:
    The Header shows "#error" instead of the string contained in the list of headers.

    What I haven't even tried to implement yet:
    Cell Formatting via expressions with the Element object - right now I don't even use the Element object.
    Sorting via expression using the First(ReportDataContainer.SortByIndexArray(0), ...)
    Hiding unused columns via a parameter and an iff statement (will hopefully be the easiest thing to implement)

    Finally, the question
    1) I guess my first question is if it is possible to do the header thing above.  Like I said, I just get #error  If it is possible, can you explain what I might be doing wrong?
    2) Also, my attempt at providing the group by index in the ReportDataContainer instead of as a parameter was a bust so any advice there would also be great (is it even possible?).
    3) My test case doesn't bother with an element object, that was to come later.  Can I even do objects inside objects, etc like that?
    4) If you see that something isn't going to work ahead of time (ex: formatting or sorting), by all means shoot me down.
    5) Does anyone have an ideas on how to specify Col width at run time?  I can get away without doing it, but I'm anal and wanted to write something as complete as possible.
    6) Is there a way to debug so I can see why a field isn't working? I didn't find much help on that on the net other then a log somewhere, but I couldn't figure out where to look and I'm not sure if it even applies to me (I'm using rdlc and data objects, not SQL Server.)

    I'm a little nervous about some information not being within the scope of the expression that would require it.  I really don't understand how all of that works in the report viewer.
    If anyone can edumicate me on some of this stuff it would be greatly appreciated. 

    I know all of this was VERY long and my questions are a bit open ended at times, but any help that can be given would be greatly appreciated.  I've bitten off more then I can chew, and my boss wants this done yesterday (isn't that how it always works?).
    For those interested in how all of this turns out, I'll post all of the code somewhere and let you know via this thread.

    Final Issue:  Please do not get to fancy with explanations - small words and big examples are greatly appreciated.  Contrary to what my lofty goal might suggest I HAVE NO IDEA WHAT I'M DOING.  There, I've said it.  I'm a total novice. I  learned 6 months ago and have been out of school for just shy of that.  I've already had my *** handed to me by ReportViewer and its disappearing data source objects (which seems to be remedied in VS2008 trial), and I've played with the table and matrix controls, and thats about it.  Frankly, my over engineering has occasionally gotten me on the big mans bad side.  This time around I was encouraged to find a general 1 size fits all solution, but I may have gone a little to general.  Oh, and though have a knack for interesting concepts, my ability to implement is . . . erm . . . "interesting" so please be patient.

    Thank you for any help/reference material you can give.  I'm just learning about ReportViewer (mostly from and don't always understand what is happening behind the scenes.

    - Jordan

    Tuesday, April 1, 2008 1:23 AM

All replies

  • *Update*
    Seem to have solved the header problem through trial and error.  Do not know why this is the case:

    Every object in the datasource array has to heave the same number of Header entries.

    That is:

    entry1.Header  = {"foo", "bar", "foorbar"}
    entry2.Header = {"filler", "filler", "filler"}

    entry1.Header = {"foo", "bar", "foorbar"}
    entry2.Header = new List(of string)

    Can someone explain why this is?
    Are there hard and fast rules for this? Does "First(...)" only work if all entries have the same number of elements?
    Tuesday, April 1, 2008 4:42 PM