Показать сообщение отдельно
Старый 14.08.2008, 16:59   #21  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Автонумерация полей при обмене значениями с записеподобными структурами
Вначале я хотел назвать заметку "Автонумерация колонок при выводе в Excel", так как подобные примеры - наиболее "на слуху", но по ходу сочинения текста появилось более общее название (хотя и менее ясное с первого взгляда).

Итак, о чем же речь? Наверняка многим, как и мне, приходилось писать код, подобный следующему:

X++:
SysExcelCells cells;
;

cells.item( row,  1).value( ledgerTrans.RecId       );
cells.item( row,  2).value( ledgerTrans.AccountNum  );
cells.item( row,  3).value( ledgerTable.AccountName );
.....................................................
cells.item( row,  8).value( ledgerTrans.Txt         );
cells.item( row,  9).value( ledgerTrans.AmountMST   );
cells.item( row, 10).value( ledgerTrans.Crediting   );
т.е. формировать (или читать) некоторую "запись", последовательно нумеруя ее поля жёсткими литералами (см. выше второй параметр метода item). Наиболее частый случай - нумерация колонок при выводе в Excel (самыми разнообразными способами). Кроме вывода в Excel это может быть, например, заполнение значениями массива (класса Array): array.value(1, ...); array.value(2, ...); и т.д. В качестве примера чтения можно привести обращение к полям записи, выбранной из ODBC-источника: ... = resultSet.getString(1).

В принципе, нам совсем не в тягость пронумеровать такие строки вручную, когда речь идет о 10 строчках (нумерация от 1 до 10). В случае сотни строк - ситуация посложнее, но все равно преодолимая. Если неохота разминать пальцы, набивая значения от 1 до 100, то можно обратиться за помощью к Excel и сгенерировать там строчки кода при помощи формулы, имея в одной колонке номер (ячейка A1: 1), в другой значения для value (B1: ledgerTrans.RecId), а в третьей - собственно формулу (C1: ="cells.item(row," & A1 & ").value(" & B1 & ");").

Неприятности начинаются, когда обнаруживается, что в уже написанный код необходимо добавить еще одну "номерную" строку в начало (середину) или поменять строки местами. Особенно "ужасна" ситуация с началом, когда требуется перебивание всех имеющихся номеров: 1 надо перебить на 2, 2 - на 3, 3 - на 4 и т.д.

И вот в один прекрасный день, копаясь в коде от нашего поставщика решения - компании GMCS, я обнаружил в схожей ситуации вывода в Excel милейшее решение проблемы. Из серии тех, до которых сам никогда не додумаешься просто потому, что никогда не задумаешься, пока не увидишь у другого.

Маленькое лирическое отступление. Мне рассказали историю про человека, который частенько оставался у знакомых в гостях на ночь и неудобно спал на маленьком диване, свернувшись калачиком. Через год он (и хозяева!) случайно узнали, что диван, оказывается, раскладной...

Увидев этот код, я испытал примерно такое же ощущение "через год"
X++:
SysExcelCells cells;
int currNum = 0;

int nextNum()
{
   currNum++;
   return currNum;
}
;

cells.item( row, nextNum() ).value( ledgerTrans.RecId       );
cells.item( row, nextNum() ).value( ledgerTrans.AccountNum  );
cells.item( row, nextNum() ).value( ledgerTable.AccountName );
.............................................................
cells.item( row, nextNum() ).value( ledgerTrans.Txt         );
cells.item( row, nextNum() ).value( ledgerTrans.AmountMST   );
cells.item( row, nextNum() ).value( ledgerTrans.Crediting)  );
Понятно, что при таком подходе можно в любой момент безболезненно переставлять строки по желанию или иной необходимости.

Хотел было здесь остановиться, да вспомнил о еще одной "тягомотине". Это уже точно только для Excel и только для тех ситуаций, когда разработчик предпочитает работать с буквенными обозначениями ячеек. При использовании класса ComExcelDocument_RU решение по аналогии c nextNum может выглядеть как-то вот так:
X++:
static void test_nextCell(Args _args)
{
    ComExcelDocument_RU doc = new ComExcelDocument_RU();
    int currCol = 0;
    int currRow = 1;

    str nextCell()
    {
        currCol++;
        return ComExcelDocument_RU::numToNameCell(currCol, currRow);
    }
    ;

    doc.NewFile();
/*
    doc.InsertValue('A1', 10);
    doc.InsertValue('B1', 20);
    doc.InsertValue('C1', 30);
    doc.InsertValue('D1', 40);
*/
    doc.InsertValue( nextCell(), 10);
    doc.InsertValue( nextCell(), 20);
    doc.InsertValue( nextCell(), 30);
    doc.InsertValue( nextCell(), 40);

    doc.finalize();
}
Хотя, конечно, следует признать, что и без того небыстрый класс здесь будет отвлекаться на довольно затратные дополнительные вычисления nextCell. Но если на минуту предположить, что при перестановке строк надо будет перебивать уже не цифры, а буквы, то поневоле согласишься с этими затратами

P.S. 04.03.09. Иногда бывает нужно нумеровать поля несколько раз внутри метода (например, первый раз при создании структуры, а затем при записи в нее). В этом случае нужно позаботиться о своевременном обнулении счетчика (currNum = 0) при повторном проходе по полям структуры.

Чтобы не выносить обнуление в отдельный оператор, можно несколько усовершенствовать функцию nextNum, добавив необязательный параметр "начать с". В этом случае в первой строке, где используется nextNum, нужно будет явно указать стартовый номер (обычно это 1), а в последующих строках применять nextNum без параметров:
X++:
SysExcelCells cells;
int currNum = 0;

int nextNum(int _beginFrom = 0)
{
    if (prmIsDefault(_beginFrom))
        currNum++;
    else
        currNum = _beginFrom;

    return currNum;
}
;

cells.item( row, nextNum(1)).value( ledgerTrans.RecId       );
cells.item( row, nextNum() ).value( ledgerTrans.AccountNum  );
cells.item( row, nextNum() ).value( ledgerTable.AccountName );
В качестве небольшого бонуса - появившаяся возможность нумеровать строки с пропуском. Например, если надо вначале пройтись по полям с 1 по 3, а затем с 9 по 11, то имеем примерно следующее:
X++:
cells.item( row, nextNum(1))...
cells.item( row, nextNum() )...
cells.item( row, nextNum() )...

cells.item( row, nextNum(9))...
cells.item( row, nextNum() )...
cells.item( row, nextNum() )...

Последний раз редактировалось Gustav; 04.03.2009 в 17:58. Причина: добавление