2009-11-20

SysTableBrowser instant field navigator

Without any doubt AX SysTableBrowser is very useful tool which helps to watch on content of exploring table. Unfortunately as for me there is one essential fault. When table contains large number of fields using SysTableBrowser sometimes it's hard to find out
a. which column corresponds to particular field; and
b. using horizontal scroll bar it's difficult to "catch up" needed field.

I must confess that using of scroll bar is inconvenient very match in case when table has a dozen and more fields.

Figure 1 demonstrates lack of information of column's headers (checkbox fields).

Figure 2 illustrates disambiguation between fields RemainSalesPhysical and RemainSalesFinancial (SalesLine Table).

The simpliest way to reach needed field is Tab button. Pressing Tab user may find needed field at the end. But if there is necessity to find another one field user has to repeat Tab-pressing another one time. And another one time. And another one time...

Take a look at Axapta Object Tree (AOT) especially at \Data Dictionary\Tables\Table\Fields node. It's easy to watch on full range of fields and move from one field to another, isn't it?

Let's combine SysTableBrowser and Table\Fields node into one window.

SysTableBrowser tool this is a conjunction of SysTableBrowser Class and SysTableBrowser Form. I decided not to touch SysTableBrowser Class and following modification this is a modification of SysTableBrowser Form only.

1. Open SysTableBrowser Form Design and add FormTreeControl TreeControl like this:

Set Left Property to Right edge.
Set Height Property to Column height.

2. We need to create new fillTree() method:

// GRR fields navigator -->
void fillTree()
{
    int _i;


    FormDesign              Ctrl = element.design();
    FormControl             _buildCtrl;

    FormControl             _formControl;
    TreeItemIdx             treeItemIdx;

    ImageListAppl_Aot       ImageListAppl_Aot;
    FormTreeItem            item;

    DictField                       dictField;

    fieldId                         fieldId;
    int                             i,j;

    int                             fieldCnt;
    #DictField
    #resAppl

    ImageRes findOverlayImage(Types _type)
    {

        ImageRes imageRes;
        switch(_type)
        {
            case Types::STRING, Types::VARSTRING :
                return #ImageOverlayString;

            case Types::ENUM:
                return #ImageOverlayEnum;

            case Types::REAL:
                return #ImageOverlayReal;

            case Types::DATE:
                return #ImageOverlayDate;

            case Types::INTEGER:
                return #ImageOverlayInteger;

            case Types::DATETIME:
                return #ImageOverlayTime;

            case Types::CONTAINER:
                return #ImageOverlayContainer;

            default:
                return 0;
        }
        return imageRes;
    }
    ;

    // Find 'Grid' object -->
    for(_i=1; _i<=Ctrl.controlCount(); _i++)
    {
        _buildCtrl = Ctrl.controlNum(_i);
        _formControl = element.control(_buildCtrl.id());
        if(_formControl.name() == 'Grid')         {             _formGridControl = _formControl;         }     }     // Find 'Grid' object <--     // Fill tree -->     if((FormTreeControl.visible())&&(dictTable))     {         ImageListAppl_Aot = new ImageListAppl_Aot();         formTreeControl.setImagelist(ImageListAppl_Aot.imageList());         formTreeControl.deleteAll();         fieldCnt = dictTable.fieldCnt();         for (i=1; i<=fieldCnt; i++)         {             fieldId = dictTable.fieldCnt2Id(i);             dictField = new DictField(dictTable.id(), fieldId);             if (bitTest(dictField.flags(), #DBF_STORE))             {                 for(j = 1;j <= dictField.arraySize(); j++)                 {                     treeItemIdx = SysFormTreeControl::addTreeItem(formTreeControl, dictField.name(), 0, null, imageListAppl_Aot.image(#ImageField));                     SysFormTreeControl::setOverlayImage(formTreeControl, treeItemIdx, imageListAppl_Aot.image(findOverlayImage(dictField.baseType())));                     itemIdxContainer += treeItemIdx;                 }             }         }         SysFormTreeControl::expandTree(formTreeControl, formTreeControl.getRoot(), 1);         // Do predefined design width more wider         element.design().width( element.design().widthValue()+FormTreeControl.widthValue() );         if(_formGridControl)             _formGridControl.topMode(FormTop::TopEdge);     }     // Fill tree <-- }

3. In run() method we'll run fillTree() method:

void run()
{
    super();
    dictTable = new DictTable(tableId);     this.parmSQLStmt('SELECT * FROM '+tableId2name(tableId));
    // GRR fields navigator -->     this.fillTree();     // GRR fields navigator <-- }

4. In ClassDeclaration we need declare two variables itemIdxContainer and _formGridControl:

class FormRun extends ObjectRun
{
    TableId tableId;
    source sqlcmd;
    DictTable dictTable;

    // GRR fields navigator -->
    Container               itemIdxContainer;
    FormGridControl         _formGridControl;
    // GRR fields navigator <--
}

5. Override mouseDblClick() method of FormTreeControl TreeControl. Now by mouse double click we may move from one field to another:

// GRR fields navigator -->
public int mouseDblClick(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift)
{
    TreeItemIdx             itemIdx;
    int ret;
    ;

    ret = super(_x, _y, _button, _Ctrl, _Shift);

    [itemIdx] = this.hitTest(_x, _y);
    if((_formGridControl)&&(itemIdx))     {         _formGridControl.controlNum( conFind(itemIdxContainer, itemIdx ) ).setFocus();     }     return ret; }

Now we may enjoy our extra functionality. If we want to hide this functionality just set FormTreeControl Visible Property to No.

I'd like to notice that navigating tree would be very useful for another tool QueryBrowser which usually deals with huge number of fields.

Who interested in another modificated SysTableBrowser take a look at Kashperuk's DEV SysTableBrowser

Copyright © 2009 Ruslan Goncharov

5 comments:

Павел said...

баг в функционале - если у поля стоит конфигурационный ключ, который делает поле невидимым (SysDeleteObjects40), то после этого поля в списке сбивается фокусировка.
решение -
1. проверять поле на видимость и доступность по конфиг ключу
2. добавлять поле в контейнер itemIdxContainer в любом случае, даже если оно невидимо в гриде

Ruslan Goncharov said...

На самом деле я не назвал бы это ошибкой в чистом виде.
Дело в том, что не смотря на то, что пользователь не видит в гриде то или иное скрытое поле,
на самом деле данная колонка в гриде присутствует, только имеет статус isDisplayed() false.
И даже если пользователь из дерева спозиционировался на колонку со статусом Отображаемое=Нет, ничего страшного
не должно произойти, просто фокус установится на эту неотображаемую колонку. По крайней мере я явной
расфокусировки не обнаружил (тестировал на 3.0, может в более высоких версиях происходит
"жесткий" перекос - не могу утверждать). То есть
когда фокус "исчезал" я понимал, что на самом деле фокус устанавливается на неотображаемой колонке.

В принципе согласен, что сам факт отображения полей в дереве, которых нет в гриде не есть хорошо.
Как вариант могу предложить следующую замену

//if (bitTest(dictField.flags(), #DBF_STORE))
if ((bitTest(dictField.flags(), #DBF_STORE)) && (bitTest(dictField.flags(), #DBF_VISIBLE)))
как в методе fillTree() формы sysTableBrowser, так и в методе showAllFields() класса sysTableBrowser

Это самое простое решение на мой взгляд. Просто первоначально принципиальной позицией было не трогать класс.

Enrico said...

Hi Ruslan,

nice work! I felt free to take your code to extend it a bit:
- field list is now sorted by name
- field can be switched on or off
- the logic how to find the correct field in the grid is complete new: instead of saving the index, I'm either setting the visible flag of the field directly on the datasource or grabbing the next field in the grid, which points to the desired field to use the setFocus function
- additionally I'm checking the visible flag of the field and if the field's configuration key is enabled

If you like, you can download the .xpo directly from my SkyDrive: http://cid-5b91a4fa21c9261d.skydrive.live.com/self.aspx/.Public/Form^_SysTableBrowser.xpo

Cheers,
Enrico

Ruslan Goncharov said...

Hi Enrico,


Thank you for reply. I'll see the project tomorrow.


Form_SysTableBrowser.xpo

http://cid-5b91a4fa21c9261d.skydrive.live.com/self.aspx/.Public/Form^_SysTableBrowser.xpo


Ruslan Goncharov

Павел said...

Проверял на 4ке, возможно в ней уже не добавляют невидимые поля, поэтому фокус начинает отставать на кол-во невидимых полей.