Search Results for

    Show / Hide Table of Contents

    Virtual datasets (VB.Net / netframework)

    Note

    This demo is available in your FlexCel installation at <FlexCel Install Folder>\samples\vb\VS2022\netframework\20.Reports\96.Virtual Datasets and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​NET-​demos/​tree/​master/​vb/​VS2022/​netframework/​Modules/​20.​Reports/​96.​Virtual Datasets

    Overview

    Normally FlexCelReport uses DataTables for its data. On reporting we can see DataTables as "overcharged" arrays, with added functionality like filtering or sorting. They are also really powerful and fast, so using them is normally the best option. But in some cases you might have very large bussiness objects and would like to use them directly without copying them first into a DataTable. On those cases, you can create your own VirtualDataset and VirtualDatasetState descendants to do this task.

    This is an advanced topic. Remember to read the [Appendix: Virtual DataSets on](~/guides/reports-developer-guide.md#appendix:-virtual-datasets -on) the Reports developer guide.

    Concepts

    • How to create a VirtualDataset descendant to encapsulate an arbitrary object and make a report with it. Two descendants are shown, one very simple with the minimum functionality needed, and other that fully implements all the features.

    • As you can see on the ComplexVirtualArrayDataSource example, to provide all the functionality you do not need to override too many methods. But you should implement very efficient methods. If you do not, probably the performance dumping everything to a DataSet would be faster.

    • How to create "infographics". this is based on the article: http://www.juiceanalytics.com/weblog/?p=236

    Files

    AssemblyInfo.vb

    Imports System.Reflection
    Imports System.Runtime.CompilerServices
    
    '
    ' General Information about an assembly is controlled through the following 
    ' set of attributes. Change these attribute values to modify the information
    ' associated with an assembly.
    '
    <Assembly: AssemblyTitle("")>
    <Assembly: AssemblyDescription("")>
    <Assembly: AssemblyConfiguration("")>
    <Assembly: AssemblyCompany("")>
    <Assembly: AssemblyProduct("")>
    <Assembly: AssemblyCopyright("(c) 2002 - 2014 TMS Software")>
    <Assembly: AssemblyTrademark("")>
    <Assembly: AssemblyCulture("")>
    
    '
    ' Version information for an assembly consists of the following four values:
    '
    '      Major Version
    '      Minor Version 
    '      Build Number
    '      Revision
    '
    ' You can specify all the values or you can default the Revision and Build Numbers 
    ' by using the '*' as shown below:
    
    <Assembly: AssemblyVersion("6.2.1.0")>
    
    '
    ' In order to sign your assembly you must specify a key to use. Refer to the 
    ' Microsoft .NET Framework documentation for more information on assembly signing.
    '
    ' Use the attributes below to control which key is used for signing. 
    '
    ' Notes: 
    '   (*) If no key is specified, the assembly is not signed.
    '   (*) KeyName refers to a key that has been installed in the Crypto Service
    '       Provider (CSP) on your machine. KeyFile refers to a file which contains
    '       a key.
    '   (*) If the KeyFile and the KeyName values are both specified, the 
    '       following processing occurs:
    '       (1) If the KeyName can be found in the CSP, that key is used.
    '       (2) If the KeyName does not exist and the KeyFile does exist, the key 
    '           in the KeyFile is installed into the CSP and used.
    '   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
    '       When specifying the KeyFile, the location of the KeyFile should be
    '       relative to the project output directory which is
    '       %Project Directory%\obj\<configuration>. For example, if your KeyFile is
    '       located in the project directory, you would specify the AssemblyKeyFile 
    '       attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
    '   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
    '       documentation for more information on this.
    '
    <Assembly: AssemblyDelaySign(False)>
    <Assembly: AssemblyKeyFile("")>
    <Assembly: AssemblyKeyName("")>
    

    ComplexVirtualArrayDataSource.vb

    Imports System.Globalization
    
    Imports FlexCel.Report
    Imports System.Collections
    
    
    Namespace VirtualDatasets
        ''' <summary>
        ''' This class implements the complete functionality needed to run a FlexCelReport from an array of objects.
        ''' Sorting/Filtering on the config sheet is allowed for this datasource, but they are not implemented on an efficient way.
        ''' If you do not have an efficient way to do those things (using indexes) and you plan to do them, you will probably get better performance 
        ''' using Datasets.
        ''' </summary>
        Public Class ComplexVirtualArrayDataSource
            Inherits VirtualDataTable
    
            #Region "Private variables"
            Private FData()() As Object
            Private FColumnCaptions() As String
            #End Region
    
            #Region "Constructors"
            Public Sub New(ByVal aCreatedBy As VirtualDataTable, ByVal aData()() As Object, ByVal aColumnCaptions() As String, ByVal aTableName As String)
                MyBase.New(aTableName, aCreatedBy)
                FData = aData
                FColumnCaptions = aColumnCaptions
            End Sub
            #End Region
    
            #Region "Columns"
            Public Overrides ReadOnly Property ColumnCount() As Integer
                Get
                    Return FColumnCaptions.Length
                End Get
            End Property
    
            Public Overrides Function GetColumn(ByVal columnName As String) As Integer
                'not very optimized method, but this is just a demo.
                If columnName Is Nothing Then
                    Return -1
                End If
                columnName = columnName.Trim()
                For i As Integer = 0 To FColumnCaptions.Length - 1
                    If String.Compare(FColumnCaptions(i), columnName, True) = 0 Then
                        Return i
                    End If
                Next i
                Return -1
            End Function
    
            Public Overrides Function GetColumnName(ByVal columnIndex As Integer) As String
                Return FColumnCaptions(columnIndex)
            End Function
    
            Public Overrides Function GetColumnCaption(ByVal columnIndex As Integer) As String
                Return FColumnCaptions(columnIndex)
            End Function
    
            #End Region
    
            #Region "Settings"
            Public Overrides ReadOnly Property Locale() As System.Globalization.CultureInfo
                Get
                    Return CultureInfo.CurrentCulture
                End Get
            End Property
            #End Region
    
            #Region "Create State"
            Public Overrides Function CreateState(ByVal sort As String, ByVal masterDetailLinks() As TMasterDetailLink, ByVal splitLink As TSplitLink) As VirtualDataTableState
                Return New ComplexVirtualArrayDataSourceState(Me, sort, masterDetailLinks, splitLink)
            End Function
    
            #End Region
    
            #Region "Data"
            Public ReadOnly Property Data() As Object()()
                Get
                    Return FData
                End Get
            End Property
    
            Public Overrides Function FilterData(ByVal newDataName As String, ByVal rowFilter As String) As VirtualDataTable
                If rowFilter Is Nothing OrElse rowFilter.Length = 0 Then
                    Return New ComplexVirtualArrayDataSource(Me, FData, FColumnCaptions, newDataName) 'no need to copy the data since it is invariant.
                End If
    
                Dim Relationship As RelationshipType = RelationshipType.Equals
                'on this demo we will only support filters of the type: "field = value", "field > value" or "field < value"
                Dim filteredData() As String = rowFilter.Split("="c)
                If filteredData Is Nothing OrElse filteredData.Length <> 2 Then
                    filteredData = rowFilter.Split("<"c)
                    If filteredData Is Nothing OrElse filteredData.Length <> 2 Then
                        filteredData = rowFilter.Split(">"c)
                        If filteredData Is Nothing OrElse filteredData.Length <> 2 Then
                            Throw New Exception("Filter """ & rowFilter & """ is invalid. The dataset """ & TableName & """ only supports filters of the type ""field =/>/< value"".")
                        Else
                            Relationship = RelationshipType.BiggerThan
                        End If
                    Else
                        Relationship = RelationshipType.LessThan
                    End If
    
                End If
    
                Dim ColIndex As Integer = GetColumn(filteredData(0).Trim())
                If ColIndex < 0 Then
                    Throw New Exception("Filter """ & rowFilter & """ is invalid. Can not find column """ & filteredData(0).Trim() & """")
                End If
    
                'Remember, this is only a demo to show what to do in this event. This code is not good code to use on 
                'a real application!. You should use some index here to find the data or this would crawl on large objects.
    
                Dim Result As New List(Of Object())()
                Dim SearchValue As String = filteredData(1).Trim()
                Dim Searchfloat As Double = 0
                If Relationship <> RelationshipType.Equals Then 'when relationship is not equals, we will assume columns are numbers. This is because we do not have any type definition for our columns on this simple example.
                    Searchfloat = Convert.ToDouble(filteredData(1).Trim())
                End If
    
                For Each Row As Object() In Data
                    Select Case Relationship
                        Case RelationshipType.Equals
                            If Convert.ToString(Row(ColIndex)) = SearchValue Then
                                Result.Add(Row) 'remember, data is invariant, so we do not need to clone Row.
                            End If
                        Case RelationshipType.LessThan
                            If Convert.ToDouble(Row(ColIndex)) < Searchfloat Then
                                Result.Add(Row) 'remember, data is invariant, so we do not need to clone Row.
                            End If
                        Case RelationshipType.BiggerThan
                            If Convert.ToDouble(Row(ColIndex)) > Searchfloat Then
                                Result.Add(Row) 'remember, data is invariant, so we do not need to clone Row.
                            End If
                    End Select
                Next Row
    
                Return New ComplexVirtualArrayDataSource(Me, Result.ToArray(), FColumnCaptions, newDataName)
    
            End Function
    
            Public Overrides Function GetDistinct(ByVal newDataName As String, ByVal filterFields() As Integer) As VirtualDataTable
                If filterFields Is Nothing OrElse filterFields.Length = 0 Then
                    Return New ComplexVirtualArrayDataSource(Me, FData, FColumnCaptions, newDataName) 'no need to copy the data since it is invariant.
                End If
    
                Dim Result As New Dictionary(Of Object() , Object())()
                Dim Keys(filterFields.Length - 1) As Object
                For Each Row As Object() In FData
                    For i As Integer = 0 To filterFields.Length - 1
                        Keys(i) = Row(filterFields(i))
                    Next i
                    Result(Keys) = Keys
                Next Row
    
                Dim R(Data.Length - 1)() As Object
                Result.Keys.CopyTo(R, 0)
    
                Dim NewColumnCaptions(filterFields.Length - 1) As String
                For i As Integer = 0 To filterFields.Length - 1
                    NewColumnCaptions(i) = FColumnCaptions(filterFields(i))
                Next i
                Return New ComplexVirtualArrayDataSource(Me, R, NewColumnCaptions, newDataName)
    
            End Function
    
            #End Region
    
        End Class
    
        Public Class ComplexVirtualArrayDataSourceState
            Inherits VirtualDataTableState
    
            #Region "Privates"
            Private SortedData()() As Object
            Private FilteredData As List(Of Object())
            #End Region
    
            #Region "Constructors"
            Public Sub New(ByVal aTableData As ComplexVirtualArrayDataSource, ByVal sort As String, ByVal masterDetailLinks() As TMasterDetailLink, ByVal splitLink As TSplitLink)
                MyBase.New(aTableData)
                If sort Is Nothing OrElse sort.Trim().Length = 0 Then
                    SortedData = aTableData.Data 'no need to clone, this is invariant.
                Else
                    SortedData = CType(aTableData.Data.Clone(), Object()())
                    Dim sortcolumn As Integer = aTableData.GetColumn(sort)
                    If sortcolumn < 0 Then
                        Throw New Exception("Can not find column """ & sort & """ in dataset """ & TableName)
                    End If
                    Array.Sort(SortedData, New ArrayComparer(sortcolumn))
                End If
    
    
                'here we should use the data in masterdetaillinks and splitlink to create indexes to make the FilteredrowCount and MoveMasterRecord methods faster.
                'on this demo we are not going to do it.
                If (masterDetailLinks IsNot Nothing AndAlso masterDetailLinks.Length > 0) OrElse splitLink IsNot Nothing Then
                    FilteredData = New List(Of Object())()
                End If
            End Sub
            #End Region
    
            #Region "Data"
            Private ReadOnly Property Data() As Object()()
                Get
                    Return CType(TableData, SimpleVirtualArrayDataSource).Data
                End Get
            End Property
    
            ''' <summary>
            ''' Remember that this method should be fast!
            ''' </summary>
            Public Overrides ReadOnly Property RowCount() As Integer
                Get
                    If FilteredData Is Nothing Then
                        Return SortedData.Length
                    End If
                    Return FilteredData.Count
                End Get
            End Property
    
            Public Overrides Function GetValue(ByVal column As Integer) As Object
                If FilteredData Is Nothing Then
                    Return SortedData(Position)(column)
                End If
                Return CType(FilteredData(Position), Object())(column)
            End Function
    
            Public Overrides Function GetValue(ByVal row As Integer, ByVal column As Integer) As Object
                If FilteredData Is Nothing Then
                    Return SortedData(row)(column)
                End If
                Return CType(FilteredData(row), Object())(column)
            End Function
            #End Region
    
            #Region "Move"
            Public Overrides Sub MoveFirst()
                'No need to do anything in an array, since "Position" is moved for us. 
                'If we had an IEnumerator for example as data backend, we would reset it here.
            End Sub
    
            Public Overrides Sub MoveNext()
                'No need to do anything in an array, since "Position" is moved for us. 
                'If we had an IEnumerator for example as data backend, we would move it here.
            End Sub
    
            #End Region
    
    
            #Region "Relationships"
            Public Overrides Sub MoveMasterRecord(ByVal masterDetailLinks() As TMasterDetailLink, ByVal splitLink As TSplitLink)
                'Here we need to modify FilteredData to contain only those records on FilteredData that are visible on this state.
                'If we created indexes on this method constructor's, this can be done fast.
    
                'on this example, as always, we will use a very slow method. The idea of this demo is not to demostrate how to do efficient code
                ' (you probably have efficient methods on the bussines objects you are wrapping), but how to override the methods.
    
                If FilteredData Is Nothing Then 'this dataset is not on master-detail relationship and does not have any split relationship either.
                    Return
                End If
    
    
                FilteredData.Clear()
                Dim ChildColumn(masterDetailLinks.Length - 1) As Integer
                For i As Integer = 0 To masterDetailLinks.Length - 1
                    ChildColumn(i) = TableData.GetColumn(masterDetailLinks(i).ChildFieldName)
                Next i
    
                Dim SplitPos As Integer = 0
                Dim StartRow As Integer = 0
                If splitLink IsNot Nothing Then
                    StartRow = splitLink.SplitCount * splitLink.ParentDataSource.Position
                End If
    
                For r As Integer = 0 To SortedData.Length - 1
                    Dim Row() As Object = SortedData(r)
                    Dim RowApplies As Boolean = True
                    For i As Integer = 0 To masterDetailLinks.Length - 1
                        Dim key As Object = masterDetailLinks(i).ParentDataSource.GetValue(masterDetailLinks(i).ParentField)
                        If Convert.ToString(Row(ChildColumn(i))) <> Convert.ToString(key) Then
                            RowApplies = False
                            Exit For
                        End If
                    Next i
    
                    If Not RowApplies Then 'the row does not fit this master detail relationship.
                        Continue For
                    End If
    
                    SplitPos += 1
                    If SplitPos <= StartRow Then 'we are not filling the correct split slot.
                        Continue For
                    End If
    
                    FilteredData.Add(Row)
                    If splitLink IsNot Nothing AndAlso FilteredData.Count >= splitLink.SplitCount Then
                        Return
                    End If
                Next r
    
    
            End Sub
    
            Public Overrides Function FilteredRowCount(ByVal masterDetailLinks() As TMasterDetailLink) As Integer
                Dim Result As Integer = 0
    
                Dim ChildColumn(masterDetailLinks.Length - 1) As Integer
                For i As Integer = 0 To masterDetailLinks.Length - 1
                    ChildColumn(i) = TableData.GetColumn(masterDetailLinks(i).ChildFieldName)
                Next i
    
                For r As Integer = 0 To SortedData.Length - 1
                    Dim Row() As Object = SortedData(r)
                    Dim RowApplies As Boolean = True
                    For i As Integer = 0 To masterDetailLinks.Length - 1
                        Dim key As Object = masterDetailLinks(i).ParentDataSource.GetValue(masterDetailLinks(i).ParentField)
                        If Convert.ToString(Row(ChildColumn(i))) <> Convert.ToString(key) Then
                            RowApplies = False
                            Exit For
                        End If
                    Next i
    
                    If Not RowApplies Then 'the row does not fit this master detail relationship.
                        Continue For
                    End If
    
                    Result += 1
                Next r
    
                Return Result
    
            End Function
    
    
            #End Region
    
        End Class
    
        Friend Enum RelationshipType
            Equals
            LessThan
            BiggerThan
        End Enum
    
        Public Class ArrayComparer
            Implements IComparer(Of Object())
    
            Private Column As Integer
            Private Shared Comparer As New CaseInsensitiveComparer()
    
            Public Sub New(ByVal aColumn As Integer)
                Column = aColumn
            End Sub
    
            Private Function IComparerGeneric_Compare(ByVal x() As Object, ByVal y() As Object) As Integer Implements IComparer(Of Object()).Compare
                Return (Comparer.Compare(x(Column), y(Column)))
            End Function
    
        End Class
    
    
    End Namespace
    

    Form1.Designer.vb

    Imports System.Collections
    Imports System.ComponentModel
    Imports System.IO
    Imports System.Reflection
    Imports System.Resources
    Imports System.Globalization
    Imports FlexCel.Core
    Imports FlexCel.XlsAdapter
    Imports FlexCel.Report
    Namespace VirtualDatasets
        Partial Public Class mainForm
            Inherits System.Windows.Forms.Form
    
            Private WithEvents button1 As System.Windows.Forms.Button
            Private saveFileDialog1 As System.Windows.Forms.SaveFileDialog
            Private label1 As System.Windows.Forms.Label
            Private WithEvents btnCancel As System.Windows.Forms.Button
            ''' <summary>
            ''' Required designer variable.
            ''' </summary>
            Private components As System.ComponentModel.Container = Nothing
    
            ''' <summary>
            ''' Clean up any resources being used.
            ''' </summary>
            Protected Overrides Sub Dispose(ByVal disposing As Boolean)
                If disposing Then
                    If components IsNot Nothing Then
                        components.Dispose()
                    End If
                End If
                MyBase.Dispose(disposing)
            End Sub
    
            #Region "Windows Form Designer generated code"
            ''' <summary>
            ''' Required method for Designer support - do not modify
            ''' the contents of this method with the code editor.
            ''' </summary>
            Private Sub InitializeComponent()
                Me.button1 = New System.Windows.Forms.Button()
                Me.saveFileDialog1 = New System.Windows.Forms.SaveFileDialog()
                Me.label1 = New System.Windows.Forms.Label()
                Me.btnCancel = New System.Windows.Forms.Button()
                Me.SuspendLayout()
                ' 
                ' button1
                ' 
                Me.button1.Anchor = (CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles))
                Me.button1.BackColor = System.Drawing.Color.Green
                Me.button1.ForeColor = System.Drawing.Color.White
                Me.button1.Location = New System.Drawing.Point(96, 80)
                Me.button1.Name = "button1"
                Me.button1.Size = New System.Drawing.Size(112, 23)
                Me.button1.TabIndex = 0
                Me.button1.Text = "GO!"
                Me.button1.UseVisualStyleBackColor = False
    '           Me.button1.Click += New System.EventHandler(Me.button1_Click)
                ' 
                ' saveFileDialog1
                ' 
                Me.saveFileDialog1.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm|Excel 97/2003|*.xls|Excel 2007|*.xlsx;*.xlsm|All files|*.*"
                Me.saveFileDialog1.RestoreDirectory = True
                ' 
                ' label1
                ' 
                Me.label1.Location = New System.Drawing.Point(24, 24)
                Me.label1.Name = "label1"
                Me.label1.Size = New System.Drawing.Size(288, 24)
                Me.label1.TabIndex = 2
                Me.label1.Text = "A demo on how to use arbitrary objects on a report. "
                ' 
                ' btnCancel
                ' 
                Me.btnCancel.Anchor = (CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles))
                Me.btnCancel.BackColor = System.Drawing.Color.FromArgb((CInt((CByte(192)))), (CInt((CByte(0)))), (CInt((CByte(0)))))
                Me.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel
                Me.btnCancel.ForeColor = System.Drawing.Color.White
                Me.btnCancel.Location = New System.Drawing.Point(216, 80)
                Me.btnCancel.Name = "btnCancel"
                Me.btnCancel.Size = New System.Drawing.Size(112, 23)
                Me.btnCancel.TabIndex = 3
                Me.btnCancel.Text = "Cancel"
                Me.btnCancel.UseVisualStyleBackColor = False
    '           Me.btnCancel.Click += New System.EventHandler(Me.btnCancel_Click)
                ' 
                ' mainForm
                ' 
                Me.AutoScaleDimensions = New System.Drawing.SizeF(6F, 13F)
                Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
                Me.ClientSize = New System.Drawing.Size(360, 130)
                Me.Controls.Add(Me.btnCancel)
                Me.Controls.Add(Me.label1)
                Me.Controls.Add(Me.button1)
                Me.Name = "mainForm"
                Me.Text = "Virtual Datasets"
                Me.ResumeLayout(False)
    
            End Sub
            #End Region
        End Class
    End Namespace
    

    Form1.vb

    Imports System.Collections
    Imports System.ComponentModel
    Imports System.IO
    Imports System.Reflection
    Imports System.Resources
    Imports System.Globalization
    Imports FlexCel.Core
    Imports FlexCel.XlsAdapter
    Imports FlexCel.Report
    
    
    Namespace VirtualDatasets
        Partial Public Class mainForm
            Inherits System.Windows.Forms.Form
    
            Public Sub New()
                InitializeComponent()
            End Sub
    
            Private Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles button1.Click
                AutoRun()
            End Sub
    
            Public Sub AutoRun()
                Dim DataPath As String = Path.Combine(Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), ".."), "..") & Path.DirectorySeparatorChar
    
                If saveFileDialog1.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
                    Dim SimpleData()() As Object = LoadDataSet(Path.Combine(DataPath, "Countries.txt"))
                    Dim SimpleTable As New SimpleVirtualArrayDataSource(Nothing, SimpleData, New String() { "Rank", "Country", "Area", "Date" }, "SimpleTable")
    
                    Using genericReport As New FlexCelReport(True)
                        genericReport.AddTable("SimpleData", SimpleTable)
    
                        Dim Complex1()() As Object = LoadDataSet(Path.Combine(DataPath, "Countries.txt"))
                        Dim ComplexAreas As New ComplexVirtualArrayDataSource(Nothing, Complex1, New String() { "Rank", "Country", "Area", "Date" }, "ComplexAreas")
                        Dim Complex2()() As Object = LoadDataSet(Path.Combine(DataPath, "Populations.txt"))
                        Dim ComplexPopulations As New ComplexVirtualArrayDataSource(Nothing, Complex2, New String() { "Rank", "Country", "Population", "Date" }, "ComplexPopulations")
    
                        genericReport.AddTable("ComplexAreas", ComplexAreas, TDisposeMode.DisposeAfterRun)
                        genericReport.AddTable("ComplexPopulations", ComplexPopulations, TDisposeMode.DisposeAfterRun)
    
    
    
                        genericReport.Run(Path.Combine(DataPath, "Virtual Datasets.template.xls"), saveFileDialog1.FileName)
                    End Using
    
                    If MessageBox.Show("Do you want to open the generated file?", "Confirm", MessageBoxButtons.YesNo) = System.Windows.Forms.DialogResult.Yes Then
                        Process.Start(saveFileDialog1.FileName)
                    End If
                End If
            End Sub
    
    
            Private Sub btnCancel_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnCancel.Click
                Close()
            End Sub
    
            Private Function LoadDataSet(ByVal filename As String) As Object()()
                'Let's create some bussiness object with random data.
    
                Dim Result As New ArrayList()
                Using sr As New StreamReader(Path.GetFullPath(filename))
                    Dim line As String
                    line = sr.ReadLine()
                    Do While line IsNot Nothing
                        Dim fields() As String = line.Split(ControlChars.Tab)
                        'Zero validation here since this is a demo and will use always the same data. On a real app you should not expect your data to play nice
                        Dim f(fields.Length - 1) As Object
                        Dim s As String = TryCast(fields(0), String)
                        f(0) = Convert.ToInt64(s)
                        f(1) = fields(1)
                        s = TryCast(fields(2), String)
                        f(2) = CObj(Convert.ToInt64(s.Replace(",", "")))
                        f(3) = fields(3)
                        Result.Add(f)
                        line = sr.ReadLine()
                    Loop
                End Using
    
                Return CType(Result.ToArray(GetType(Object())), Object()())
            End Function
        End Class
    
    End Namespace
    

    Program.vb

    Namespace VirtualDatasets
        Friend NotInheritable Class Program
    
            Private Sub New()
            End Sub
    
            ''' <summary>
            ''' The main entry point for the application.
            ''' </summary>
           <STAThread> _
            Shared Sub Main()
                Application.EnableVisualStyles()
                Application.SetCompatibleTextRenderingDefault(False)
                Application.Run(New mainForm())
            End Sub
        End Class
    End Namespace
    

    SimpleVirtualArrayDataSource.vb

    Imports System.Globalization
    
    Imports FlexCel.Report
    
    Namespace VirtualDatasets
        ''' <summary>
        ''' This class implements the minimum functionality needed to run a FlexCelReport from an array of objects.
        ''' No Sorting/Filtering on the config sheet is allowed for this datasource, and you can not use it on master detail relationships.
        ''' </summary>
        Public Class SimpleVirtualArrayDataSource
            Inherits VirtualDataTable
    
            #Region "Private variables"
            Private FData()() As Object
            Private FColumnCaptions() As String
            #End Region
    
            #Region "Constructors"
            Public Sub New(ByVal aCreatedBy As VirtualDataTable, ByVal aData()() As Object, ByVal aColumnCaptions() As String, ByVal aTableName As String)
                MyBase.New(aTableName, aCreatedBy)
                FData = aData
                FColumnCaptions = aColumnCaptions
            End Sub
            #End Region
    
            #Region "Columns"
            Public Overrides ReadOnly Property ColumnCount() As Integer
                Get
                    Return FColumnCaptions.Length
                End Get
            End Property
    
            Public Overrides Function GetColumn(ByVal columnName As String) As Integer
                'not very optimized method, but this is just a demo.
                If columnName Is Nothing Then
                    Return -1
                End If
                columnName = columnName.Trim()
                For i As Integer = 0 To FColumnCaptions.Length - 1
                    If String.Compare(FColumnCaptions(i), columnName, True) = 0 Then
                        Return i
                    End If
                Next i
                Return -1
            End Function
    
            Public Overrides Function GetColumnName(ByVal columnIndex As Integer) As String
                Return FColumnCaptions(columnIndex)
            End Function
    
            Public Overrides Function GetColumnCaption(ByVal columnIndex As Integer) As String
                Return FColumnCaptions(columnIndex)
            End Function
    
            #End Region
    
            #Region "Settings"
            Public Overrides ReadOnly Property Locale() As System.Globalization.CultureInfo
                Get
                    Return CultureInfo.CurrentCulture
                End Get
            End Property
            #End Region
    
            #Region "Create State"
            Public Overrides Function CreateState(ByVal sort As String, ByVal masterDetailLinks() As TMasterDetailLink, ByVal splitLink As TSplitLink) As VirtualDataTableState
                Return New SimpleVirtualArrayDataSourceState(Me)
            End Function
    
            #End Region
    
            #Region "Data"
            Public ReadOnly Property Data() As Object()()
                Get
                    Return FData
                End Get
            End Property
            #End Region
    
            #Region "Filter"
            ''' <summary>
            ''' Even when we don't implement filter, we need to return a new instance when rowFilter is null.
            ''' </summary>
            ''' <param name="newDataName"></param>
            ''' <param name="rowFilter"></param>
            ''' <returns></returns>
            Public Overrides Function FilterData(ByVal newDataName As String, ByVal rowFilter As String) As VirtualDataTable
                If String.IsNullOrEmpty(rowFilter) Then
                    Return New SimpleVirtualArrayDataSource(Me, Data, FColumnCaptions, TableName)
                End If
                Return MyBase.FilterData(newDataName, rowFilter)
            End Function
            #End Region
    
        End Class
    
        Public Class SimpleVirtualArrayDataSourceState
            Inherits VirtualDataTableState
    
            #Region "Constructors"
            Public Sub New(ByVal aTableData As SimpleVirtualArrayDataSource)
                MyBase.New(aTableData)
            End Sub
            #End Region
    
            #Region "Data"
            Private ReadOnly Property Data() As Object()()
                Get
                    Return CType(TableData, SimpleVirtualArrayDataSource).Data
                End Get
            End Property
    
            ''' <summary>
            ''' Remember that this method should be fast!
            ''' </summary>
            Public Overrides ReadOnly Property RowCount() As Integer
                Get
                    Return Data.Length
                End Get
            End Property
    
            Public Overrides Function GetValue(ByVal column As Integer) As Object
                Return Data(Position)(column)
            End Function
    
            Public Overrides Function GetValue(ByVal row As Integer, ByVal column As Integer) As Object
                Return Data(row)(column)
            End Function
    
            Public Overrides Sub MoveFirst()
                'No need to do anything in an array, since "Position" is moved for us. 
                'If we had an IEnumerator for example as data backend, we would reset it here.
            End Sub
    
            Public Overrides Sub MoveNext()
                'No need to do anything in an array, since "Position" is moved for us. 
                'If we had an IEnumerator for example as data backend, we would move it here.
            End Sub
    
            #End Region
    
        End Class
    
    End Namespace
    
    In This Article
    Back to top FlexCel Studio for the .NET Framework v7.24.0.0
    © 2002 - 2025 tmssoftware.com