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