Камасутра с объектами Worksheet и Range в Excel

в 4:08, , рубрики: Delphi, Excel, range, worksheet, Совершенный код, метки: ,

Делая раз за разом поисковые запросы вида «delphi excel range» я обнаружил потрясающую бедность, скудоумие и безыдейность предлагаемых примеров готового программного кода. В лучшем случае речь идёт о паре-тройке операций вывода в произвольную ячейку, обо всяких нюансах типа форматирования и доступа к объектам Excel можно… не то чтобы забыть. Авторы ничтоже сумняшеся предлагают переносить код, сгенерированный записью макроса, с учётом синтаксиса Дельфи, в код приложения, которое печатает отчёт. Причём в большинстве примеров используется позднее связывание, которое скрадывает некоторые наиболее ужасные моменты переноса, однако такой код работает далеко не идентично коду на VBA и далеко не все операции обрабатываются должным образом. В результате мы получаем неоправданно громоздкие исходники, компилирующиеся только под определённой версией Delphi/RAD Studio и работающие только с определённой версией Офиса.

Кто не сталкивался с такой конструкцией, разбирая чужие исходники?

Worksheet.Range[Worksheet.Cells[i, 1], Worksheet.Cells[i, MaxCols]].MergeCells := True;
Worksheet.Range[Worksheet.Cells[i, 1], Worksheet.Cells[i, MaxCols]].HorizontalAlignment := xlCenter;
Worksheet.Range[Worksheet.Cells[i, 1], Worksheet.Cells[i, MaxCols]].VerticalAlignment := xlCenter;
for j := 1 to MaxCols do Worksheet.Cells[i + 1, j].Value := j;

В данном случае в некоем цикле построчно объединяются и выравниваются по центру ячейки со столбца A до MaxCols и в следующей строке печатаются номера столбцов. И вроде как работает она иногда, а иногда не работает. У меня, например, ячейки объединяются, а выравнивание вызывает ошибку. Причём ячейки выделяются строго таким образом в любом примере. А что если в параметры Range попадёт не тот Worksheet? А что если нам с данным диапазоном ещё 100500 операций делать?

Иногда спасает, конечно, конструкция with, но она тоже не идеальна. Выравнивание в ней всё равно не работает. Да и не во всех языках она имеется. Корни зла таятся в матричной адресации ячеек и в позднем связывании. Перепишем код для раннего связывания:

Worksheet.Range[Worksheet.Cells.Item[i, 1], Worksheet.Cells.Item[i, MaxCols]].MergeCells := True;
Worksheet.Range[Worksheet.Cells.Item[i, 1], Worksheet.Cells.Item[i, MaxCols]].HorizontalAlignment := xlCenter;
Worksheet.Range[Worksheet.Cells.Item[i, 1], Worksheet.Cells.Item[i, MaxCols]].VerticalAlignment := xlCenter;
for j := 1 to MaxCols do Worksheet.Cells.Item[i + 1, j].Value[xlRangeValueDefault] := j;

Кошмар продолжается. Добавилось мерзкое .Item. Однако выравнивание заработало. Ладно, не будем о грустном. Настоящее дао построения отчётов на Excel из Delphi открывается, когда знакомишься поближе с объектом Range, а точнее с его свойствами Resize и Offset. Наш кусок кода превращается в довольно элегантное:

// хотелось бы Range := Worksheet.Cells.Item[i, 1].Resize[1, MaxCols], но не судьба :(
Range := Worksheet.Range[Worksheet.Cells.Item[i, 1], EmptyParam].Resize[1, MaxCols];
Range.MergeCells := True;
Range.HorizontalAlignment := xlCenter;
Range.VerticalAlignment := xlCenter;
Range := Range.Resize[1, 1];
for j := 1 to MaxCols do Range.Offset[1, j - 1].Value2 := j;

Желающим поэкспериментировать также предлагаю вариант с автозаполнением ячеек:

// вместо цикла for:
Range := Range.Offset[1, 0].Resize[1, 1];
Range.Value2 := 1;
Range.AutoFill(Range.Resize[1, MaxCols], xlFillSeries);

Ну а чтобы не заморачиваться с оборачиванием Worksheet.Cells.Item в Worksheet.Range, стоит задуматься о создании шаблона отчёта с именованными диапазонами. Это позволит также сэкономить на программном форматировании кода. Но об этом — в следующий раз.

Автор: hopungo

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js