Wish [B4X] Resize B4XTable columns based on content, title included

LucaMs

Expert
Licensed User
Longtime User
With reference to the following snippet:
[B4X] B4XTable - Resize columns based on content

I wanted the column width adjustment to also include the header.

I would suggest to replace that snippet with the following Sub:
B4X:
Public Sub XTableDataUpdated(XTable As B4XTable, TitleIncluded As Boolean)
    Dim cvsB4XTable As B4XCanvas
    cvsB4XTable.Initialize(XTable.mBase)

    Dim ShouldRefresh As Boolean

    Dim CellLabel As B4XView
    Dim CellText As String
    Dim HeaderLabel As B4XView
    Dim HeaderText As String

    For Each column As B4XTableColumn In XTable.Columns
        Dim MaxWidth As Int

        If TitleIncluded Then
            HeaderText = column.CellsLayouts.Get(0).As(B4XView).GetView(0).Text
            HeaderLabel = column.CellsLayouts.Get(0).As(B4XView).GetView(0)
            MaxWidth = cvsB4XTable.MeasureText(HeaderText, HeaderLabel.Font).Width + 10dip
        End If
 
        For i = 0 To XTable.VisibleRowIds.Size - 1
            Dim RowId As Long = XTable.VisibleRowIds.Get(i)
            If RowId > 0 Then
                CellLabel = column.CellsLayouts.Get(i + 1).As(B4XView).GetView(0)
                If column.ColumnType <> XTable.COLUMN_TYPE_DATE Then
                    CellText = XTable.GetRow(RowId).Get(column.Id)
                Else
                    CellText = DateTime.Date(XTable.GetRow(RowId).Get(column.Id))
                End If
                MaxWidth = Max(MaxWidth, cvsB4XTable.MeasureText(CellText, CellLabel.Font).Width + 10dip)
            End If
        Next

        If MaxWidth > column.ComputedWidth Or MaxWidth < column.ComputedWidth - 20dip Then
            column.Width = MaxWidth
            ShouldRefresh = True
        End If

    Next

    If ShouldRefresh Then
        XTable.Refresh
    End If

End Sub

Obviously you should call it from the DataUpdated event of the B4XTable:
B4X:
Private Sub B4XTable1_DataUpdated
    XTableDataUpdated(B4XTable1, True)
End Sub
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
That snippet (the original, not this one) also has a problem, it doesn't work well with date columns.
The width of the "tick text" is measured instead of the displayed text.
Original:
Dim pnl As B4XView = column.CellsLayouts.Get(i + 1)
Dim lbl As B4XView = pnl.GetView(0)
Dim txt As String = B4XTable1.GetRow(RowId).Get(column.Id)
MaxWidth = Max(MaxWidth, cvs.MeasureText(txt, lbl.Font).Width + 10dip)
I think it should be:
B4X:
Dim pnl As B4XView = column.CellsLayouts.Get(i + 1)
Dim lbl As B4XView = pnl.GetView(0)
If column.ColumnType <> B4XTable1.COLUMN_TYPE_DATE Then
    Dim txt As String = B4XTable1.GetRow(RowId).Get(column.Id)
Else
    Dim txt As String = DateTime.Date(B4XTable1.GetRow(RowId).Get(column.Id))
End If
MaxWidth = Max(MaxWidth, cvs.MeasureText(txt, lbl.Font).Width + 10dip)
 
Last edited:

Mahares

Expert
Licensed User
Longtime User
Obviously you should call it from the DataUpdated event of the B4XTable:
You are on a mission Mario. But, don't you think it is more complete if you have the option not to autosize each column:
This is how I approach it:
To call it, I use one of these two lines:
B4X:
    XTableDataUpdated(B4XTable1, B4XTable1.Columns,True)    'to apply it to all columns
    XTableDataUpdated(B4XTable1, Array(B4XTable1.GetColumn("COL2"), B4XTable1.GetColumn("COL4")),True)   'To apply it to select columns
B4X:
Public Sub XTableDataUpdated(XTable As B4XTable, lstColumns As List, TitleIncluded As Boolean)
    Dim cvsB4XTable As B4XCanvas
    cvsB4XTable.Initialize(XTable.mBase)
    Dim ShouldRefresh As Boolean
    Dim CellLabel As B4XView
    Dim CellText As String
    Dim HeaderLabel As B4XView
    Dim HeaderText As String

    For Each column As B4XTableColumn In lstColumns        
            Dim MaxWidth As Int
            If TitleIncluded Then
                HeaderText = column.CellsLayouts.Get(0).As(B4XView).GetView(0).Text
                HeaderLabel = column.CellsLayouts.Get(0).As(B4XView).GetView(0)
                MaxWidth = cvsB4XTable.MeasureText(HeaderText, HeaderLabel.Font).Width + 10dip
            End If
            For i = 0 To XTable.VisibleRowIds.Size - 1
                Dim RowId As Long = XTable.VisibleRowIds.Get(i)
                If RowId > 0 Then
                    CellLabel = column.CellsLayouts.Get(i + 1).As(B4XView).GetView(0)
                    If column.ColumnType <> XTable.COLUMN_TYPE_DATE Then
                        CellText = XTable.GetRow(RowId).Get(column.Id)
                    Else
                        CellText = DateTime.Date(XTable.GetRow(RowId).Get(column.Id))
                    End If
                    MaxWidth = Max(MaxWidth, cvsB4XTable.MeasureText(CellText, CellLabel.Font).Width + 10dip)
                End If
            Next
            If MaxWidth > column.ComputedWidth Or MaxWidth < column.ComputedWidth - 20dip Then
                column.Width = MaxWidth
                ShouldRefresh = True
            End If
    Next
    If ShouldRefresh Then
        XTable.Refresh
    End If
End Sub
 
Top