As developers and consultants often require the ability to check the content of hidden fields without writing a select statement / query, or taking a quick look in the database for retrieving information, I was wondering if there is a way to do that.

The SysTableBrowser class is using the FormBuildControl.addDataField(int dataSourceId, fieldId fieldId) method for bound and FormBuildControl.addControl(FormControlType controlType, str controlName) as an unbound control. These methods are on kernel level, and they are forcing to hide any bound controls where the field property Visible is set to No.

We can add the hidden fields as extra columns in the SysTableBrowser class with a highlighted color, but as the display grid is tied to the datasource we are using it is not possible to populate it with the right values per record.

protected void AddField(tableId tableId,
    fieldId fieldId,
    FormBuildDataSource formBuildDataSource,
    FormBuildGridControl formBuildGridControl,
    boolean allowControlEdit)
{
    FormBuildStringControl formBuildStringControl;
    Object formBuildControl;
    SysDictField dictField;
    int j;
    // WIK -->
    FormControlType     formControlType;
    // WIK <--
    ;

    dictField = new SysDictField(tableId, fieldId);
    if (dictField.saveContents())
    {

// (...)

                    formBuildControl.displayLengthValue(5);
                    formBuildControl.lookupButton(2);
                }

            }

            // WIK -->
            if (! dictField.visible())
            {
                switch (dictField.baseType())
                {
                    case Types::Integer :       formControlType = FormControlType::Integer; break;
                    case Types::Int64  :        formControlType = FormControlType::Int64; break;
                    case Types::Guid :          formControlType = FormControlType::Guid; break;
                    case Types::Real :          formControlType = FormControlType::Real; break;
                    case Types::Date :          formControlType = FormControlType::Date; break;
                    case Types::Time :          formControlType = FormControlType::Time; break;
                    case Types::UtcDateTime :   formControlType = FormControlType::DateTime; break;
                    default :                   formControlType = FormControlType::String;
                }

                formBuildControl = formBuildGridControl.addControl(formControlType, dictField.name());
                formBuildControl.label(dictField.name());
                formBuildControl.colorScheme(FormColorScheme::RGB);
                formBuildControl.backgroundColor(WinAPI::rgb2int(220, 70, 70));
                formBuildControl.helpText(dictField.label());
                formBuildControl.allowEdit(false);
            }
            // WIK <--
        }
    }
}

What we can do though is create a temporary table where we reference the current table, record and field identifiers, and store the relevant data in the dedicated base type fields:

Then we have to add our temporary table to the \AOTFormsSysTableBrowser form, prepare the link to the datasource by the record id reference in it’s initialization:

public void init()
{
    QueryBuildDataSource        qbds;
    ;

    super();

    qbds    = TmpSysTableBrowser_ds.query().dataSourceTable(tablenum(TmpSysTableBrowser));
    // QueryBuildRange qbr -- Global declaration
    qbr     = SysQuery::findOrCreateRange(qbds, fieldnum(TmpSysTableBrowser, RefRecId));
}

When the actual cursor moves on a record (the FormDataSource.active() method is called), we want to populate our table with the right data, then it can be displayed in the TmpGrid grid control which is hidden by default:

public int active()
{
    TmpSysTableBrowser  tmpSysTableBrowserLocal;
    DictTable           dictTableLocal;
    SysDictField        dictField;
    int                 i;
    int                 fieldCnt;
    int                 ret;
    ;

    ret = super();

    element.lockWindowUpdate(true);

    dictTableLocal  = new DictTable(ds.TableId);
    fieldCnt        = dictTableLocal.fieldCnt();

    for (i = 1; i <= fieldCnt; i++)
    {
        dictField   = new SysDictField(ds.TableId, dictTableLocal.fieldCnt2Id(i));

        if (dictField && ! dictField.visible())
        {
            tmpSysTableBrowserLocal.clear();
            switch (dictField.baseType())
            {
                case Types::Integer :
                    tmpSysTableBrowserLocal.IntegerValue    = ds.(dictField.id());
                    TmpSysTableBrowser_IntegerValue.visible(true);
                    break;
                case Types::Guid :
                    tmpSysTableBrowserLocal.GuidValue       = ds.(dictField.id());
                    TmpSysTableBrowser_GuidValue.visible(true);
                    break;
                case Types::Real :
                    tmpSysTableBrowserLocal.RealValue       = ds.(dictField.id());
                    TmpSysTableBrowser_RealValue.visible(true);
                    break;
                case Types::Date :
                    tmpSysTableBrowserLocal.DateValue       = ds.(dictField.id());
                    TmpSysTableBrowser_DateValue.visible(true);
                    break;
                case Types::Time :
                    tmpSysTableBrowserLocal.TimeValue       = ds.(dictField.id());
                    TmpSysTableBrowser_TimeValue.visible(true);
                    break;
                case Types::UtcDateTime :
                    tmpSysTableBrowserLocal.DateTimeValue   = ds.(dictField.id());
                    TmpSysTableBrowser_DateTimeValue.visible(true);
                    break;
                case Types::Int64 :
                    tmpSysTableBrowserLocal.Int64Value   = ds.(dictField.id());
                    TmpSysTableBrowser_Int64Value.visible(true);
                    break;
                case Types::Container :
                    tmpSysTableBrowserLocal.StringValue  = con2str(ds.(dictField.id()));
                    TmpSysTableBrowser_StringValue.visible(true);
                    break;
                default :
                    tmpSysTableBrowserLocal.StringValue  = strfmt('%1', ds.(dictField.id()));
                    TmpSysTableBrowser_StringValue.visible(true);
            }

            tmpSysTableBrowserLocal.FieldName    = dictField.name();
            tmpSysTableBrowserLocal.FieldLabel   = dictField.label();
            tmpSysTableBrowserLocal.DictTableId  = dictTableLocal.id();
            tmpSysTableBrowserLocal.FieldId      = dictField.id();
            tmpSysTableBrowserLocal.RefRecId     = ds.RecId;
            tmpSysTableBrowserLocal.insert();
        }
    }

    if (tmpSysTableBrowserLocal)
    {
        qbr.value(SysQuery::value(ds.RecId));
        TmpSysTableBrowser.setTmpData(tmpSysTableBrowserLocal);
        TmpSysTableBrowser_ds.executeQuery();

        TmpGrid.visible(true);
    }

    element.lockWindowUpdate(false);

    return ret;
}

The final result looks like this, all of our non-visible fields are added to the Table browser and we can see the rest of the data too:

The XPO file is available on the following link for download, with column highlighting and hidden group hiding additions
PrivateProject_WIK_SysTableBrowser.xpo

Hope you like the tool, you may leave feedbacks as comments.