B4A Library [Class] Flexible Table

This thread will be used by Erel, Melamoud and myself to discuss / post new releases of the Table class.

The table class is a flexible UI component that enable scrollable table like UI, with sortable columns, multiselect rows etc

the table is very efficient, maintaining labels only for visible rows

old thread with details : http://www.b4x.com/forum/additional...view-supports-tables-any-size.html#post110901

The class depend on following libraries:
- StringUtils (standard)
- SQL (standard)
- JavaObject (standard)
- ScrollView2D (additional)

List of major features.
1. scrollable table UI
2. sortable columns
3. select a row, cell or multi select rows
4. callback for selection / click a cell / row
5. callback for long click action
6. read / write to CSV file

Current version --> 3.33 Custom View
Current version --> 1.44 Class

Other complementary routines:

Load data with the Remote Database Connector.


EDIT: LucaMs has written a routine to fill a table with a Remote Database Connector query result see post 182.
The routine hasn't been added into the Class for the reasons explained in post 183.
A sample program can be found HERE.

Code:
B4X:
'load data from a RDC Request
'Result = DBResult object got from a RDC request
'AutomaticWidths  True > set the column widths automaticaly
'Written by LucasMs
Public Sub LoadRDCResult(Result As DBResult, AutomaticWidths As Boolean)
    cAutomaticWidths = AutomaticWidths
    NumberOfColumns = Result.Columns.Size
    innerClearAll(NumberOfColumns)

    Dim Headers(NumberOfColumns) As String
    Dim ColumnWidths(NumberOfColumns) As Int
    Dim HeaderWidths(NumberOfColumns) As Int
    Dim DataWidths(NumberOfColumns) As Int
    Dim col, row As Int
    Dim str As String
    For col = 0 To NumberOfColumns - 1
        Headers(col) = Result.Columns.GetKeyAt(col)
        If AutomaticWidths = False Then
            ColumnWidths(col) = 130dip
            HeaderWidths(col) = 130dip
            DataWidths(col) = 130dip
        Else
            HeaderWidths(col) = cvs.MeasureStringWidth(Headers(col), Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth
            DataWidths(col) = 0

            Dim FieldValue As Object
            For row = 0 To Result.Rows.Size - 1
                Dim Record() As Object = Result.Rows.Get(row)
                FieldValue = Record(col)
                If GetType(FieldValue) = "java.lang.String" Then
                    DataWidths(col) = Max(DataWidths(col), cvs.MeasureStringWidth(str, Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth)
                End If
            Next
            ColumnWidths(col) = Max(HeaderWidths(col), DataWidths(col))
        End If
    Next
    SetHeader(Headers)
    SetColumnsWidths(ColumnWidths)

    For Each Record() As Object In Result.Rows
        Dim R(NumberOfColumns) As String
        Dim FieldV As String
        For col = 0 To NumberOfColumns - 1
            FieldV = Record(col)
            R(col) = FieldV
        Next
        AddRow(R)
    Next
End Sub

This is another routine updated by cimperia in post #392 using a Map for the columns and a List for the rows.
B4X:
'load data from a RDC Request
'A RDC request returns a DBResult object, therefore this method
'could be called as is:
'LoadRDCResult(DBResult.Columns, DBResult.Rows, True)
'AutomaticWidths  True > set the column widths automaticaly
'Written by LucasMs
Public Sub LoadRDCResult(Columns As Map, Rows As List, AutomaticWidths As Boolean)
  cAutomaticWidths = AutomaticWidths
  NumberOfColumns = Columns.Size
  innerClearAll(NumberOfColumns)

  Dim Headers(NumberOfColumns) As String
  Dim ColumnWidths(NumberOfColumns) As Int
  Dim HeaderWidths(NumberOfColumns) As Int
  Dim DataWidths(NumberOfColumns) As Int
  Dim col, row As Int
  Dim str As String
  For col = 0 To NumberOfColumns - 1
    Headers(col) = Columns.GetKeyAt(col)
    If AutomaticWidths = False Then
      ColumnWidths(col) = 130dip
      HeaderWidths(col) = 130dip
      DataWidths(col) = 130dip
    Else
      HeaderWidths(col) = cvs.MeasureStringWidth(Headers(col), Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth
      DataWidths(col) = 0

      Dim FieldValue As Object
      For row = 0 To Rows.Size - 1
        Dim Record() As Object = Rows.Get(row)
        FieldValue = Record(col)
       If GetType(FieldValue) = "java.lang.String" Then
         DataWidths(col) = Max(DataWidths(col), cvs.MeasureStringWidth(str, Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth)
       End If
      Next
      ColumnWidths(col) = Max(HeaderWidths(col), DataWidths(col))
    End If
  Next
  SetHeader(Headers)
  SetColumnsWidths(ColumnWidths)

  For Each Record() As Object In Rows
    Dim R(NumberOfColumns) As String
    Dim FieldV As String
    For col = 0 To NumberOfColumns - 1
      FieldV = Record(col)
      R(col) = FieldV
    Next
    AddRow(R)
  Next
End Sub


Load data from a MSMariaDB database.

Another routine for loading data from a MSMariaDB database can be found in post#727.
Thanks to @Magma.

Updates:
EDIT: 2024.01.13 Version 3.33
Changed possible values for DataType
TEXT and NUMBER become T, R and I
Amended problem with column colors
Amended problems with SetHeaderColors and SetHeaderTextColors

Version 3.32
Amended Header and HeaderFirst problem in SaveCSVFromTable
Moved If (lblStatusline... from AddRow to ShowRow

Version 3.31
Added SingleLine property for the Designer
Added StatusLineHeight as a property
Added FastScrollLabelMaxChars as a property

EDIT: 2021.06.28 Version 3.30
Added a check for none numeric values for numeric sorting.

EDIT: 2021.06.28 Version 3.29
Amended problem with column colors
Version 3.28
Added NumberOfColumns in the code
Added TopRowIndex method
Version 3.27
Amended MultiSelect EDIT: 2020.09.02 Version 3.26
Amended problem with sort with remove accents
Amended problem with SetRowColorN
Added SetCellAlignmentColN method
Added SetHeaderAlignmentColN method

EDIT: 2020.08.05 Version 3.24
Amended problem with JumpToRowAndSelect not being selected.
Amended error when setting RowHeight before the table initialized

EDIT: 2020.06.19 Version 3.22
Amended error in the insertRowAt routine.

EDIT: 2020.05.25 Version 3.21
Amended bug with TextSize in fixed columns

EDIT: 2020.05.16 Version 3.20
Added fast scroll feature
Version 3.19
Improved automatic width calculation and hidden columns
Version 3.18
Added a check in RemoveRowColorN to ensure that Row is not out of bounds
Added ShowRow event
Amended automatic width calculations
Amended hidden column width problem

EDIT: 2020.04.21 Version 3.17
Amended HeaderHight problem with fixed columns

EDIT: 2020.04.21 Version 3.16
Amended two errors.

EDIT: 2020.04.14 Version 3.14
Added the methods below
- LoadSQLiteDB4(SQLite As SQL, Query As String, AutomaticWidths As Boolean)
loads SQLite data with data type checking
- LoadSQLiteDB5(SQLite As SQL, Query As String, Values() As String, AutomaticWidths As Boolean).
loads SQLite data with data type checking , similar to LoadSQLiteDB4 but for parametrized queries.
- GetColumnDataTypes As String(), returns an Array with the data type for each column.
- GetColumnDataType(Column As Int) As String, returns the data type of the fiven column.
Added the InnerTotalWidth property, read only.
Added multiple first fiexed columns
Added line colors

EDIT: 2020.03.10 Version 3.10
Amended bug reported HERE

EDIT: 2020.03.06 Version 3.09
Amended bug reported HERE.

EDIT: 2020.02.29 Version 3.08
Amended SetHeaderTypefaces method problem reprted HERE.
Added HeaderTypeface property.

EDIT: 2020.01.08 Version 3.07
Amended bug ShowStatisLine = False property bug.
Added MultiSelect property to Designer properties.
You need to open and close the Designer when you use the new version the first time to make the MultiSelect property active.

EDIT: 2019.12.28 Version 3.06
Amened some bugs

EDIT: 2019.12.25 Version 3.05
Added FirstColumnFixed property which allows to fix the first column.
Attention: You need to open and close the Designer to make the new property active.

EDIT: 2019.11.15 Version 3.04
- Added SelectedRowTextColor and SlectedCellTextColor properties
- Added ZeroSelections property, True > when a selected row is pressed it will be unselected False > it remains selected.

EDIT: 2019.11.12 Version 3.03
- Changed JumpToRowAndSelect(Row As Int, Col As Int) to JumpToRowAndSelect(Col As Int, Row As Int)
- Changed LoadSQLiteDB2 signature. Replaced the possible values from "T", "I", "R" to "TEXT", "NUMBER" for coherence with SetColumnDataTypes.
- Added internal sorting bitmaps, avoids loading the image files into the Files folder.
- Added two new properties: SortBitmapWidth and SortBitmapColor.
- Added SetCustomSortingBitmaps method, which allows to use custom bitmaps instead of the internal ones.
Attention: You need to open and close the Designer to make the new properties active.
Attention: You need to invert the parameters in JumpToRowAndSelect.

EDIT: 2019.07.04 Version 3.02
Amended error reported in post #887

EDIT: 2019.06.26 Version 3.01
Amended SingleLine property setting in the code

EDIT: 2019.04.05 Version 3.00
Amended SetColumnColors and SetTextColors
Removed Reflection library dependency

EDIT: 2018.04.11 Version 2.29
Version 2.27
set the two variables sortedCol and sortingDir to Public instaed of Private
added RemoveAccent routine for sorting with accented characters
Version 2.28
Added SetHeaderTypeFaces
Added SortRemoveAccents property
Version 2.29
Added SaveTableToCSV2 with a user defined separator character

EDIT: 2018.04.11 Version 2.26
added LoadSQLiteDB3 method using SQLExec2 instead of SQLExec
The query can include question marks which will be replaced with the values in the array.

EDIT: 2018.03.27 Version 2.25
amended minor errors
added UpdateCell method

EDIT: 2017.11.19 Version 2.22
improved JumpToRowAndSelect scrolls horizontally to the selected column
improved setHeaderHeight
added padding for status bar Label

EDIT: 2017.06.27 Version 2.19
Replaced DoEvents by Sleep(0)
Asked HERE

EDIT: 2017.06.27 Version 2.19
Replaced DoEvents by Sleep(0)
Asked HERE

EDIT: 2017.05.16 Version 2.18
Amended error reported HERE.

EDIT: 2017.03.09 Version 2.17
Amended error reported HERE.

EDIT: 2017.03.09 Version 2.15
Amended error reported here, Event signatures
#Event: CellClick(col As Int, row As Int)
#Event: CellLongClick(col As Int, row As Int)

EDIT: 2016.12.05 Version 2.14
Added NumberOfColumns and NumberOfRows as Public variables.
Amended error reported here.

EDIT: 2016.12.05 Version 2.13
Amended error reported here.
Added NumberOfColumns as a property for the Designer.

EDIT: 2016.07.30 Version 2.10
Amended error with TextAlignment and HeaderTextAlignment reported in post #606

EDIT: 2016.03.15 Version 2.00
Added CustomView support.
This version can be compiled into a library.
Changes between the previous versions and version 2.00
For a Table added in the Designer, this is new
No need to initialize nor add it onto a parent view
'For a Table added in the Designer, this is new
'No need to initialize nor add it onto a parent view

For a Table added in the code:
The Initialize routine has been splittend into two routines.
New:
Initialize (CallBack As Object, EventName As String)
InitializeTable (vNumberOfColumns As Int, cellAlignement As Int, showStatusL As Boolean)

'Example:
Table1.Initialize(Me, "Table1")
Table1.InitializeTable(5, Gravity.CENTER_HORIZONTAL, True)


Old:
Initialize(CallBack As Object, EventName As String, vNumberOfColumns As Int, cellAlignement As Int, showStatusL As Boolean)
Example:
Table1.Initialize(Me, "Table1", 5, Gravity.CENTER_HORIZONTAL, True)

EDIT: 2015.04.29 Version 1.43
As the modifications in LoadSQLiteDB don't work in all cases I went back.
LoadSQLiteDB as in version 1.40
Added LoadSQLiteDB2 where the column data types must be given.

EDIT: 2015.04.26 Version 1.42
Changed he LoadSQLiteDB routine, version 1.41 didn't work as expected.
The final solution was suggested by cimperia HERE.

EDIT: 2015.04.16 Version 1.41
Changed the LoadSQLiteDB routine according to the error reported in the SQL issue thread
and the SQLite Cursor GetString versus GetDouble thread.
The problem appears with numbers bigger than 999999.
I left version 1.40 in case of problems.
I tested it with a few databases, but I am not sure if it works in all cases.

EDIT: 2015.03.05
Amended bugs reported in posts #383 and #386
Added SetAutomaticWidths routine

EDIT: 2015.02.19
Amended the problem alignment reported in post # 378

EDIT: 2015.02.13
Amended the problem of rows not shown reported in post # 371

EDIT: 2015.01.09
Added header aligments

EDIT: 2014.08.14
Added HeaderHeight property
Amended RowColor problem reported in post #260

EDIT: 2014.08.10
Added SortColumn property asked in post #266
Added UseColumnColors ColumnColors and HeaderColors propeties

EDIT: 2014.05.10 Added RowHeight as a property

Screenshot:

1589638570453.png
 

Attachments

  • TableV1_44.zip
    44.8 KB · Views: 1,971
  • 1589638550715.png
    1589638550715.png
    31.8 KB · Views: 1,398
  • TestFastScroll.zip
    50.6 KB · Views: 1,286
  • Table.bas
    136.3 KB · Views: 90
  • TableV3_33.zip
    106.4 KB · Views: 131
Last edited:

klaus

Expert
Licensed User
Longtime User
The Table class needs following libraries:
JavaObject internal
SQL internal
StringUtils internal
ScrollViewd2D additional.
You need to download the ScrollView2D.jar and ScrollView2D.xml files and copy them into the AdditionalLibraries\B4X folder.
Check these libraries in the Libraries Manager tab in the IDE.
For explanations about libraries, you may have a look HERE.
 

Oscarin

Member
The Table class needs following libraries:
JavaObject internal
SQL internal
StringUtils internal
ScrollViewd2D additional.
You need to download the ScrollView2D.jar and ScrollView2D.xml files and copy them into the AdditionalLibraries\B4X folder.
Check these libraries in the Libraries Manager tab in the IDE.
For explanations about libraries, you may have a look HERE.
Thanks, but I ended up using

SD FlexGrid (Table) library​

 

RB Smissaert

Well-Known Member
Licensed User
Longtime User
This thread will be used by Erel, Melamoud and myself to discuss / post new releases of the Table class.

The table class is a flexible UI component that enable scrollable table like UI, with sortable columns, multiselect rows etc

the table is very efficient, maintaining labels only for visible rows

old thread with details : http://www.b4x.com/forum/additional...view-supports-tables-any-size.html#post110901

The class depend on following libraries:
- StringUtils (standard)
- SQL (standard)
- JavaObject (standard)
- ScrollView2D (additional)

List of major features.
1. scrollable table UI
2. sortable columns
3. select a row, cell or multi select rows
4. callback for selection / click a cell / row
5. callback for long click action
6. read / write to CSV file

Current version --> 3.30 Custom View
Current version --> 1.44 Class

Other complementary routines:

Load data with the Remote Database Connector.


EDIT: LucaMs has written a routine to fill a table with a Remote Database Connector query result see post 182.
The routine hasn't been added into the Class for the reasons explained in post 183.
A sample program can be found HERE.

Code:
B4X:
'load data from a RDC Request
'Result = DBResult object got from a RDC request
'AutomaticWidths  True > set the column widths automaticaly
'Written by LucasMs
Public Sub LoadRDCResult(Result As DBResult, AutomaticWidths As Boolean)
    cAutomaticWidths = AutomaticWidths
    NumberOfColumns = Result.Columns.Size
    innerClearAll(NumberOfColumns)

    Dim Headers(NumberOfColumns) As String
    Dim ColumnWidths(NumberOfColumns) As Int
    Dim HeaderWidths(NumberOfColumns) As Int
    Dim DataWidths(NumberOfColumns) As Int
    Dim col, row As Int
    Dim str As String
    For col = 0 To NumberOfColumns - 1
        Headers(col) = Result.Columns.GetKeyAt(col)
        If AutomaticWidths = False Then
            ColumnWidths(col) = 130dip
            HeaderWidths(col) = 130dip
            DataWidths(col) = 130dip
        Else
            HeaderWidths(col) = cvs.MeasureStringWidth(Headers(col), Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth
            DataWidths(col) = 0

            Dim FieldValue As Object
            For row = 0 To Result.Rows.Size - 1
                Dim Record() As Object = Result.Rows.Get(row)
                FieldValue = Record(col)
                If GetType(FieldValue) = "java.lang.String" Then
                    DataWidths(col) = Max(DataWidths(col), cvs.MeasureStringWidth(str, Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth)
                End If
            Next
            ColumnWidths(col) = Max(HeaderWidths(col), DataWidths(col))
        End If
    Next
    SetHeader(Headers)
    SetColumnsWidths(ColumnWidths)

    For Each Record() As Object In Result.Rows
        Dim R(NumberOfColumns) As String
        Dim FieldV As String
        For col = 0 To NumberOfColumns - 1
            FieldV = Record(col)
            R(col) = FieldV
        Next
        AddRow(R)
    Next
End Sub

This is another routine updated by cimperia in post #392 using a Map for the columns and a List for the rows.
B4X:
'load data from a RDC Request
'A RDC request returns a DBResult object, therefore this method
'could be called as is:
'LoadRDCResult(DBResult.Columns, DBResult.Rows, True)
'AutomaticWidths  True > set the column widths automaticaly
'Written by LucasMs
Public Sub LoadRDCResult(Columns As Map, Rows As List, AutomaticWidths As Boolean)
  cAutomaticWidths = AutomaticWidths
  NumberOfColumns = Columns.Size
  innerClearAll(NumberOfColumns)

  Dim Headers(NumberOfColumns) As String
  Dim ColumnWidths(NumberOfColumns) As Int
  Dim HeaderWidths(NumberOfColumns) As Int
  Dim DataWidths(NumberOfColumns) As Int
  Dim col, row As Int
  Dim str As String
  For col = 0 To NumberOfColumns - 1
    Headers(col) = Columns.GetKeyAt(col)
    If AutomaticWidths = False Then
      ColumnWidths(col) = 130dip
      HeaderWidths(col) = 130dip
      DataWidths(col) = 130dip
    Else
      HeaderWidths(col) = cvs.MeasureStringWidth(Headers(col), Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth
      DataWidths(col) = 0

      Dim FieldValue As Object
      For row = 0 To Rows.Size - 1
        Dim Record() As Object = Rows.Get(row)
        FieldValue = Record(col)
       If GetType(FieldValue) = "java.lang.String" Then
         DataWidths(col) = Max(DataWidths(col), cvs.MeasureStringWidth(str, Typeface.DEFAULT, cTextSize) + 8dip + cLineWidth)
       End If
      Next
      ColumnWidths(col) = Max(HeaderWidths(col), DataWidths(col))
    End If
  Next
  SetHeader(Headers)
  SetColumnsWidths(ColumnWidths)

  For Each Record() As Object In Rows
    Dim R(NumberOfColumns) As String
    Dim FieldV As String
    For col = 0 To NumberOfColumns - 1
      FieldV = Record(col)
      R(col) = FieldV
    Next
    AddRow(R)
  Next
End Sub


Load data from a MSMariaDB database.

Another routine for loading data from a MSMariaDB database can be found in post#727.
Thanks to @Magma.

Updates:
EDIT: 2021.06.28 Version 3.29
Added a check for none numeric values for numeric sorting.

EDIT: 2021.06.28 Version 3.29
Amended problem with column colors
Version 3.28
Added NumberOfColumns in the code
Added TopRowIndex method
Version 3.27
Amended MultiSelect EDIT: 2020.09.02 Version 3.26
Amended problem with sort with remove accents
Amended problem with SetRowColorN
Added SetCellAlignmentColN method
Added SetHeaderAlignmentColN method

EDIT: 2020.08.05 Version 3.24
Amended problem with JumpToRowAndSelect not being selected.
Amended error when setting RowHeight before the table initialized

EDIT: 2020.06.19 Version 3.22
Amended error in the insertRowAt routine.

EDIT: 2020.05.25 Version 3.21
Amended bug with TextSize in fixed columns

EDIT: 2020.05.16 Version 3.20
Added fast scroll feature
Version 3.19
Improved automatic width calculation and hidden columns
Version 3.18
Added a check in RemoveRowColorN to ensure that Row is not out of bounds
Added ShowRow event
Amended automatic width calculations
Amended hidden column width problem

EDIT: 2020.04.21 Version 3.17
Amended HeaderHight problem with fixed columns

EDIT: 2020.04.21 Version 3.16
Amended two errors.

EDIT: 2020.04.14 Version 3.14
Added the methods below
- LoadSQLiteDB4(SQLite As SQL, Query As String, AutomaticWidths As Boolean)
loads SQLite data with data type checking
- LoadSQLiteDB5(SQLite As SQL, Query As String, Values() As String, AutomaticWidths As Boolean).
loads SQLite data with data type checking , similar to LoadSQLiteDB4 but for parametrized queries.
- GetColumnDataTypes As String(), returns an Array with the data type for each column.
- GetColumnDataType(Column As Int) As String, returns the data type of the fiven column.
Added the InnerTotalWidth property, read only.
Added multiple first fiexed columns
Added line colors

EDIT: 2020.03.10 Version 3.10
Amended bug reported HERE

EDIT: 2020.03.06 Version 3.09
Amended bug reported HERE.

EDIT: 2020.02.29 Version 3.08
Amended SetHeaderTypefaces method problem reprted HERE.
Added HeaderTypeface property.

EDIT: 2020.01.08 Version 3.07
Amended bug ShowStatisLine = False property bug.
Added MultiSelect property to Designer properties.
You need to open and close the Designer when you use the new version the first time to make the MultiSelect property active.

EDIT: 2019.12.28 Version 3.06
Amened some bugs

EDIT: 2019.12.25 Version 3.05
Added FirstColumnFixed property which allows to fix the first column.
Attention: You need to open and close the Designer to make the new property active.

EDIT: 2019.11.15 Version 3.04
- Added SelectedRowTextColor and SlectedCellTextColor properties
- Added ZeroSelections property, True > when a selected row is pressed it will be unselected False > it remains selected.

EDIT: 2019.11.12 Version 3.03
- Changed JumpToRowAndSelect(Row As Int, Col As Int) to JumpToRowAndSelect(Col As Int, Row As Int)
- Changed LoadSQLiteDB2 signature. Replaced the possible values from "T", "I", "R" to "TEXT", "NUMBER" for coherence with SetColumnDataTypes.
- Added internal sorting bitmaps, avoids loading the image files into the Files folder.
- Added two new properties: SortBitmapWidth and SortBitmapColor.
- Added SetCustomSortingBitmaps method, which allows to use custom bitmaps instead of the internal ones.
Attention: You need to open and close the Designer to make the new properties active.
Attention: You need to invert the parameters in JumpToRowAndSelect.

EDIT: 2019.07.04 Version 3.02
Amended error reported in post #887

EDIT: 2019.06.26 Version 3.01
Amended SingleLine property setting in the code

EDIT: 2019.04.05 Version 3.00
Amended SetColumnColors and SetTextColors
Removed Reflection library dependency

EDIT: 2018.04.11 Version 2.29
Version 2.27
set the two variables sortedCol and sortingDir to Public instaed of Private
added RemoveAccent routine for sorting with accented characters
Version 2.28
Added SetHeaderTypeFaces
Added SortRemoveAccents property
Version 2.29
Added SaveTableToCSV2 with a user defined separator character

EDIT: 2018.04.11 Version 2.26
added LoadSQLiteDB3 method using SQLExec2 instead of SQLExec
The query can include question marks which will be replaced with the values in the array.

EDIT: 2018.03.27 Version 2.25
amended minor errors
added UpdateCell method

EDIT: 2017.11.19 Version 2.22
improved JumpToRowAndSelect scrolls horizontally to the selected column
improved setHeaderHeight
added padding for status bar Label

EDIT: 2017.06.27 Version 2.19
Replaced DoEvents by Sleep(0)
Asked HERE

EDIT: 2017.06.27 Version 2.19
Replaced DoEvents by Sleep(0)
Asked HERE

EDIT: 2017.05.16 Version 2.18
Amended error reported HERE.

EDIT: 2017.03.09 Version 2.17
Amended error reported HERE.

EDIT: 2017.03.09 Version 2.15
Amended error reported here, Event signatures
#Event: CellClick(col As Int, row As Int)
#Event: CellLongClick(col As Int, row As Int)

EDIT: 2016.12.05 Version 2.14
Added NumberOfColumns and NumberOfRows as Public variables.
Amended error reported here.

EDIT: 2016.12.05 Version 2.13
Amended error reported here.
Added NumberOfColumns as a property for the Designer.

EDIT: 2016.07.30 Version 2.10
Amended error with TextAlignment and HeaderTextAlignment reported in post #606

EDIT: 2016.03.15 Version 2.00
Added CustomView support.
This version can be compiled into a library.
Changes between the previous versions and version 2.00
For a Table added in the Designer, this is new
No need to initialize nor add it onto a parent view
'For a Table added in the Designer, this is new
'No need to initialize nor add it onto a parent view

For a Table added in the code:
The Initialize routine has been splittend into two routines.
New:
Initialize (CallBack As Object, EventName As String)
InitializeTable (vNumberOfColumns As Int, cellAlignement As Int, showStatusL As Boolean)

'Example:
Table1.Initialize(Me, "Table1")
Table1.InitializeTable(5, Gravity.CENTER_HORIZONTAL, True)


Old:
Initialize(CallBack As Object, EventName As String, vNumberOfColumns As Int, cellAlignement As Int, showStatusL As Boolean)
Example:
Table1.Initialize(Me, "Table1", 5, Gravity.CENTER_HORIZONTAL, True)

EDIT: 2015.04.29 Version 1.43
As the modifications in LoadSQLiteDB don't work in all cases I went back.
LoadSQLiteDB as in version 1.40
Added LoadSQLiteDB2 where the column data types must be given.

EDIT: 2015.04.26 Version 1.42
Changed he LoadSQLiteDB routine, version 1.41 didn't work as expected.
The final solution was suggested by cimperia HERE.

EDIT: 2015.04.16 Version 1.41
Changed the LoadSQLiteDB routine according to the error reported in the SQL issue thread
and the SQLite Cursor GetString versus GetDouble thread.
The problem appears with numbers bigger than 999999.
I left version 1.40 in case of problems.
I tested it with a few databases, but I am not sure if it works in all cases.

EDIT: 2015.03.05
Amended bugs reported in posts #383 and #386
Added SetAutomaticWidths routine

EDIT: 2015.02.19
Amended the problem alignment reported in post # 378

EDIT: 2015.02.13
Amended the problem of rows not shown reported in post # 371

EDIT: 2015.01.09
Added header aligments

EDIT: 2014.08.14
Added HeaderHeight property
Amended RowColor problem reported in post #260

EDIT: 2014.08.10
Added SortColumn property asked in post #266
Added UseColumnColors ColumnColors and HeaderColors propeties

EDIT: 2014.05.10 Added RowHeight as a property

Screenshot:

View attachment 94167
Using this class, still on version 2.26 as I made several modifcations of the code.
There is a bug to do with the Sub AddRow that can cause a serious slowdown of loading a large table.

The last few lines of that Sub should be:

B4X:
    If cShowStatusLine Then
        If (lblStatusLine.IsInitialized And enableStatusLineAutoFill) Then setStatusLine(Data.Size & " rows") ' should this be automatic ?
    End If

Omitting: If cShowStatusLine Then
Means the status label is updated for every row of the provided data, even if you don't want the status label, so even if cShowStatusLine = False

Also these lines:

[CODE
SV2.Panel.Height = Data.Size * cRowHeight
SVF.Panel.Height = SV2.Panel.Height
updateIPLocation
[/CODE]

I think, can be taken out of the Sub AddRow and be added instead to the end of the code in ShowRow, with a small performance gain.

Unless I am mistaken the above still applies to the latest version. 3.30.

RBS
 

klaus

Expert
Licensed User
Longtime User
Unless I am mistaken the above still applies to the latest version. 3.30.
It is still there.

Have you tested it ?
ShowRow is called many times during scrolling, do you see any scroll performance loss ?
How many rows are you loading ?
 

RB Smissaert

Well-Known Member
Licensed User
Longtime User
It is still there.

Have you tested it ?
ShowRow is called many times during scrolling, do you see any scroll performance loss ?
How many rows are you loading ?
Yes, tested and all working fine.
As said, the main thing is to do with cShowStatusLine.

RBS
 

RB Smissaert

Well-Known Member
Licensed User
Longtime User
OK, will post.

RBS
This is all the code to do with the table class and outside my current table class:

In Main:
---------------------------

B4X:
Sub tbl_Cell_Click(oTbl As Table, RC As RowCol)
    
    strClickedTable = oTbl.Panel.Tag
    
    Select strClickedTable
        Case "tblMeds"
            iMedListIndex = RC.Row
        Case "tblIssues"
            iIssueListIndex = RC.Row
            strSelectedIssueDrug = oTbl.GetValue(1, iIssueListIndex)
        Case "tblIssuesDistinct"
            iIssueListIndex = RC.Row
        Case "tblValues"
            strClickedValueDate = oTbl.GetValue(0, RC.Row)
            strClickedValueName = oTbl.GetValue(1, RC.Row)
            
            'dClickedValue is only used for if altering values
            '-------------------------------------------------
            Try
                dClickedValue = oTbl.GetValue(2, RC.Row)
            Catch
                dClickedValue = 0
            End Try
            iNum_Values_RowID = tblValues.GetValue(6, RC.Row)
        Case "tblFilteredValues"
            strClickedValueDate = oTbl.GetValue(0, RC.Row)
            strClickedValueName = oTbl.GetValue(RC.Col, RC.Row)
            
            'dClickedValue is only used for if altering values
            '-------------------------------------------------
            Try
                dClickedValue = oTbl.GetValue(RC.Col, RC.Row)
            Catch
                dClickedValue = 0
            End Try
            iNum_Values_RowID = oTbl.GetValue(RS_FilteredValues.ColumnCount - 1, RC.Row)
        Case "tblSavedSQL"
            iSavedSQLRow = RC.Row
        Case "tblFolders"
            Dim bTblFoldersMultiSelect As Boolean = oTbl.SelectedRows.Size > 1
            btnDefaultFolder.Visible = (bTblFoldersMultiSelect = False)
        Case "tblPatsAtAddress"
            lblAddress.Text = AddressFromID(oTbl.GetValue(0, RC.Row))
    End Select
    
    If bCollectTableCellsForSQLInClause Then
        oListTableCells.Add(oTbl.GetValue(RC.Col, RC.Row))
    End If
    
End Sub

Sub tbl_Cell_LongClick(oTbl As Table, RC As RowCol)
    
    Select oTbl.Panel.tag
        Case "tblValues", "tblUniqueValues", "tblFilteredValues", "tblMeds", "tblIssues", "tblIssuesDistinct", "tblIssuesSingle", _
             "tblProblems", "tblBP", "tblAppointments", "tblReferrals", "tblConsultations", "tblSavedSQL", "tblRegHistory", _
             "tblDiary", "tblVaccinations"
            ShowTableCell(oTbl, RC, 2)
        Case "tblDialog"
            If RC.Col > 0 Then
                ShowTableCell(oTbl, RC, 2)
            Else
                LoadPatientFromTable(oTbl, RC)
            End If
        Case "tblPatientList", "tblPatsAtAddress", "tblPats", "tblSQLResult", "tblPreviousPatients"
            LoadPatientFromTable(oTbl, RC)
    End Select
    
End Sub

In a code module called General:
---------------------------------------------

B4X:
Sub Process_Globals

    Public mapAlteredTableRowColour As Map
    Public mapAlteredTableTextColour As Map
    Public mapArrAlteredRowColour As Map
    Public mapArrAlteredRowTextColour As Map
    
    Public mapRowsItalic As Map
    Public mapArrRowsItalic As Map

    Public iMinSortThreshold As Int

    Public iMaxColumnWidth As Int
    Public bMultiColumnSort As Boolean

    Public bSortTableCaseInsensitive As Boolean

    Private ExcelUnixDiff As Long = 25569       'days between Jan 01 1900 and Jan 01 1970
    Private DaySecs As Long = 86400             'number of seconds in a day
    Private DayMiliSecs As Long = 86400000      'number of milli-seconds in a day
    
    Public bAllowOnlyRealXLDates As Boolean
    Public strTableDateExample As String
    Public strTableDateFormat As String

End Sub

Sub Init_General
    
     mapAlteredTableRowColour.Initialize
    mapAlteredTableTextColour.Initialize
    mapArrAlteredRowColour.Initialize
    mapArrAlteredRowTextColour.Initialize
    
    mapRowsItalic.Initialize
    mapArrRowsItalic.Initialize
    
End Sub

Public Sub ExcelDate2Age(iExcelDate As Int) As Int
    
    Dim lTicks As Long
    Dim iYear As Int
    Dim iMonth As Int
    Dim iDay As Int
    Dim iAge As Int
    
    lTicks = ExcelDate2Ticks(iExcelDate)
    iYear = DateTime.GetYear(lTicks)
    iMonth = DateTime.GetMonth(lTicks)
    iAge = Starter.lTodayYear - iYear
    
    If Starter.lTodayMonth < iMonth Then
        iAge = iAge - 1
    Else
        If Starter.lTodayMonth = iMonth Then
            iDay = DateTime.GetDayOfMonth(lTicks)
            If iDay > Starter.lTodayDay Then
                iAge = iAge - 1
            End If
        End If
    End If
    
    Return iAge

End Sub

Public Sub ExcelDate2String(lXLDate As Long) As String

    Dim lVal As Long
    
    If bAllowOnlyRealXLDates Then
        If lXLDate < 1 Then
            Return ""
        End If
    End If
    
    lVal = (lXLDate - ExcelUnixDiff) * DaySecs
    lVal = DateUtils.UnixTimeToTicks(lVal)

    Return(DateTime.Date(lVal))
    
End Sub

Public Sub Ticks2String(lTicks As Long, strFormat As String, bMegaTicks As Boolean) As String
    
    'RunLog("Ticks2String, lTicks: " & lTicks)
    
    If lTicks = 0 Then 'but this could be 01/01/1970 midnight!
        Return ""
    End If
    
    If strFormat.Length = 0 Then
        strFormat = "dd/MMM/yyyy"
    End If
    
    DateTime.DateFormat = strFormat
    
    If bMegaTicks Then
        Return DateTime.Date(lTicks * 1000)
    Else
        Return DateTime.Date(lTicks)
    End If
    
End Sub

Public Sub ArrayType(Var As Object) As String
    
    Dim Res As String

    Dim VarType As String = GetType(Var)

    If VarType.StartsWith("[") Then

        Dim SecondChar As String = VarType.SubString2(1,2)
        Select Case SecondChar
            Case "B"
                Res = "Byte"
            Case "S"
                Res = "Short"
            Case "I"
                Res = "Int"
            Case "J"
                Res = "Long"
            Case "F"
                Res = "Float"
            Case "D"
                Res = "Double"
            Case "C"
                Res = "Char"
            Case "L"
                If VarType.Contains("String") Then
                    Res = "String"
                Else
                    Res = "Object"
                End If
            Case Else
                Res = ""
        End Select

    End If

    Return Res

End Sub

Public Sub FormatMilliSecs(lMilliSecs As Long) As String

    Dim iSeconds As Int
    Dim iMinutes As Int
    Dim iHours As Int
    
    If lMilliSecs < 1000 Then
        Return lMilliSecs & " milli-seconds"
    End If
    
    If lMilliSecs < 10000 Then
        Return Round(lMilliSecs / 1000) & " seconds"
    End If
    
    If lMilliSecs < 60000 Then
        Return Round(lMilliSecs / 1000) & " seconds"
    End If
    
    If lMilliSecs < 3600000 Then
        iMinutes = lMilliSecs / 60000
        iSeconds = (lMilliSecs - (iMinutes * 60000)) / 1000
        Return iMinutes & " minutes, " & iSeconds  & " seconds"
    End If
    
    iHours = lMilliSecs / 3600000
    iMinutes = (lMilliSecs - iHours * 3600000) / 60000
    
    Return iHours & " hours, " & iMinutes  & " minutes"
    
End Sub

Then I attached my current table class as I have it now and also a sorting class with the sorting algo's.
I do realize that it might be somewhat tricky to convert the sorting code in the latest table class, but I think
it may be worth it.

RBS
 

Attachments

  • Table.bas
    208.1 KB · Views: 101
  • clsSorting.bas
    28.7 KB · Views: 96

RB Smissaert

Well-Known Member
Licensed User
Longtime User
This is all the code to do with the table class and outside my current table class:

In Main:
---------------------------

B4X:
Sub tbl_Cell_Click(oTbl As Table, RC As RowCol)
   
    strClickedTable = oTbl.Panel.Tag
   
    Select strClickedTable
        Case "tblMeds"
            iMedListIndex = RC.Row
        Case "tblIssues"
            iIssueListIndex = RC.Row
            strSelectedIssueDrug = oTbl.GetValue(1, iIssueListIndex)
        Case "tblIssuesDistinct"
            iIssueListIndex = RC.Row
        Case "tblValues"
            strClickedValueDate = oTbl.GetValue(0, RC.Row)
            strClickedValueName = oTbl.GetValue(1, RC.Row)
           
            'dClickedValue is only used for if altering values
            '-------------------------------------------------
            Try
                dClickedValue = oTbl.GetValue(2, RC.Row)
            Catch
                dClickedValue = 0
            End Try
            iNum_Values_RowID = tblValues.GetValue(6, RC.Row)
        Case "tblFilteredValues"
            strClickedValueDate = oTbl.GetValue(0, RC.Row)
            strClickedValueName = oTbl.GetValue(RC.Col, RC.Row)
           
            'dClickedValue is only used for if altering values
            '-------------------------------------------------
            Try
                dClickedValue = oTbl.GetValue(RC.Col, RC.Row)
            Catch
                dClickedValue = 0
            End Try
            iNum_Values_RowID = oTbl.GetValue(RS_FilteredValues.ColumnCount - 1, RC.Row)
        Case "tblSavedSQL"
            iSavedSQLRow = RC.Row
        Case "tblFolders"
            Dim bTblFoldersMultiSelect As Boolean = oTbl.SelectedRows.Size > 1
            btnDefaultFolder.Visible = (bTblFoldersMultiSelect = False)
        Case "tblPatsAtAddress"
            lblAddress.Text = AddressFromID(oTbl.GetValue(0, RC.Row))
    End Select
   
    If bCollectTableCellsForSQLInClause Then
        oListTableCells.Add(oTbl.GetValue(RC.Col, RC.Row))
    End If
   
End Sub

Sub tbl_Cell_LongClick(oTbl As Table, RC As RowCol)
   
    Select oTbl.Panel.tag
        Case "tblValues", "tblUniqueValues", "tblFilteredValues", "tblMeds", "tblIssues", "tblIssuesDistinct", "tblIssuesSingle", _
             "tblProblems", "tblBP", "tblAppointments", "tblReferrals", "tblConsultations", "tblSavedSQL", "tblRegHistory", _
             "tblDiary", "tblVaccinations"
            ShowTableCell(oTbl, RC, 2)
        Case "tblDialog"
            If RC.Col > 0 Then
                ShowTableCell(oTbl, RC, 2)
            Else
                LoadPatientFromTable(oTbl, RC)
            End If
        Case "tblPatientList", "tblPatsAtAddress", "tblPats", "tblSQLResult", "tblPreviousPatients"
            LoadPatientFromTable(oTbl, RC)
    End Select
   
End Sub

In a code module called General:
---------------------------------------------

B4X:
Sub Process_Globals

    Public mapAlteredTableRowColour As Map
    Public mapAlteredTableTextColour As Map
    Public mapArrAlteredRowColour As Map
    Public mapArrAlteredRowTextColour As Map
   
    Public mapRowsItalic As Map
    Public mapArrRowsItalic As Map

    Public iMinSortThreshold As Int

    Public iMaxColumnWidth As Int
    Public bMultiColumnSort As Boolean

    Public bSortTableCaseInsensitive As Boolean

    Private ExcelUnixDiff As Long = 25569       'days between Jan 01 1900 and Jan 01 1970
    Private DaySecs As Long = 86400             'number of seconds in a day
    Private DayMiliSecs As Long = 86400000      'number of milli-seconds in a day
   
    Public bAllowOnlyRealXLDates As Boolean
    Public strTableDateExample As String
    Public strTableDateFormat As String

End Sub

Sub Init_General
   
     mapAlteredTableRowColour.Initialize
    mapAlteredTableTextColour.Initialize
    mapArrAlteredRowColour.Initialize
    mapArrAlteredRowTextColour.Initialize
   
    mapRowsItalic.Initialize
    mapArrRowsItalic.Initialize
   
End Sub

Public Sub ExcelDate2Age(iExcelDate As Int) As Int
   
    Dim lTicks As Long
    Dim iYear As Int
    Dim iMonth As Int
    Dim iDay As Int
    Dim iAge As Int
   
    lTicks = ExcelDate2Ticks(iExcelDate)
    iYear = DateTime.GetYear(lTicks)
    iMonth = DateTime.GetMonth(lTicks)
    iAge = Starter.lTodayYear - iYear
   
    If Starter.lTodayMonth < iMonth Then
        iAge = iAge - 1
    Else
        If Starter.lTodayMonth = iMonth Then
            iDay = DateTime.GetDayOfMonth(lTicks)
            If iDay > Starter.lTodayDay Then
                iAge = iAge - 1
            End If
        End If
    End If
   
    Return iAge

End Sub

Public Sub ExcelDate2String(lXLDate As Long) As String

    Dim lVal As Long
   
    If bAllowOnlyRealXLDates Then
        If lXLDate < 1 Then
            Return ""
        End If
    End If
   
    lVal = (lXLDate - ExcelUnixDiff) * DaySecs
    lVal = DateUtils.UnixTimeToTicks(lVal)

    Return(DateTime.Date(lVal))
   
End Sub

Public Sub Ticks2String(lTicks As Long, strFormat As String, bMegaTicks As Boolean) As String
   
    'RunLog("Ticks2String, lTicks: " & lTicks)
   
    If lTicks = 0 Then 'but this could be 01/01/1970 midnight!
        Return ""
    End If
   
    If strFormat.Length = 0 Then
        strFormat = "dd/MMM/yyyy"
    End If
   
    DateTime.DateFormat = strFormat
   
    If bMegaTicks Then
        Return DateTime.Date(lTicks * 1000)
    Else
        Return DateTime.Date(lTicks)
    End If
   
End Sub

Public Sub ArrayType(Var As Object) As String
   
    Dim Res As String

    Dim VarType As String = GetType(Var)

    If VarType.StartsWith("[") Then

        Dim SecondChar As String = VarType.SubString2(1,2)
        Select Case SecondChar
            Case "B"
                Res = "Byte"
            Case "S"
                Res = "Short"
            Case "I"
                Res = "Int"
            Case "J"
                Res = "Long"
            Case "F"
                Res = "Float"
            Case "D"
                Res = "Double"
            Case "C"
                Res = "Char"
            Case "L"
                If VarType.Contains("String") Then
                    Res = "String"
                Else
                    Res = "Object"
                End If
            Case Else
                Res = ""
        End Select

    End If

    Return Res

End Sub

Public Sub FormatMilliSecs(lMilliSecs As Long) As String

    Dim iSeconds As Int
    Dim iMinutes As Int
    Dim iHours As Int
   
    If lMilliSecs < 1000 Then
        Return lMilliSecs & " milli-seconds"
    End If
   
    If lMilliSecs < 10000 Then
        Return Round(lMilliSecs / 1000) & " seconds"
    End If
   
    If lMilliSecs < 60000 Then
        Return Round(lMilliSecs / 1000) & " seconds"
    End If
   
    If lMilliSecs < 3600000 Then
        iMinutes = lMilliSecs / 60000
        iSeconds = (lMilliSecs - (iMinutes * 60000)) / 1000
        Return iMinutes & " minutes, " & iSeconds  & " seconds"
    End If
   
    iHours = lMilliSecs / 3600000
    iMinutes = (lMilliSecs - iHours * 3600000) / 60000
   
    Return iHours & " hours, " & iMinutes  & " minutes"
   
End Sub

Then I attached my current table class as I have it now and also a sorting class with the sorting algo's.
I do realize that it might be somewhat tricky to convert the sorting code in the latest table class, but I think
it may be worth it.

RBS
The posted file Table.bas had faulty code in the 2 Subs that passed a map a B4XOrderedMap to the table class and this has been fixed.
Nil to do with the sorting code, but I thought best to correct it.

RBS
 

Attachments

  • Table.bas
    186.6 KB · Views: 98

Sergey_New

Well-Known Member
Licensed User
Longtime User
Thank you! The table had a hidden column (width=0), now it has appeared. Is it possible to set this column to its original width=0?
 

Sergey_New

Well-Known Member
Licensed User
Longtime User
I have an old version of the table where there is no hideColumn :(
P.S. Found: hideCol :)
 
Last edited:

JoséCarlos

Member
Licensed User
Need help.
I have many small CSV files.
As there are many, I want to include it in KVS and I would like to know if there is a solution to be able to open it through TableV3_30?
Thanks.
 

klaus

Expert
Licensed User
Longtime User
Sorry, but i do not really understand your problem.
You have many csv files.
What do you include in KVS ?
I have never used KVS.
What do you mean with: open it through TableV3_30
You can easily read a csv file into a Table CustomView.
Do you want to show several or even many Tables at the same time ?
Depending on the answers to the questions above, the solution could / would be different.
I am afraid that for small csv files, the Table CustomView is too big.
I have CustomViews for Tables with different levels, not yet published, and cross-platform to accomplish what you want.
But i need more detailed information on what exactly you want to do and what you expect.
Can you post a small project showing either what you have already done and what you want to achieve.
Maybe you could have a look at this thread: https://www.b4x.com/android/forum/threads/slow-loading-clv-with-table-solved.144928/page-2#posts
 

JoséCarlos

Member
Licensed User
Good Morning.
Thanks for your response.
I have several .csv files.
a.csv, b.csv, c.csv, d.csv............
I stored them all in KeyValueStore:
kvs.Put("a", a.csv)
kvs.Put("b", b.csv)
kvs.Put("c", c.csv)
kvs.Put("d", d.csv).........

In your example in TableV3_30 we used:
Table1.LoadTableFromCSV2(File.DirInternal, "a.csv", True, ";", True) to a.csv
Table1.LoadTableFromCSV2(File.DirInternal, "b.csv", True, ";", True) to b.csv.....


My question can be asked:
kvs.Get("a"), kvs.Get("b"), kvs.Get("c"), kvs.Get("d"),....
Thanks.
 

klaus

Expert
Licensed User
Longtime User
As i have never used KeyValueStore it is difficult to give a concrete advice.
You say that you have :
I have several .csv files.
a.csv, b.csv, c.csv, d.csv.

What is the content of a.csv, b.csv etc ?
Are these file names or what else.
When you call kvs.Get("a"), what do you get in return ?
 
Top