Delphi excel range all

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Сегодняшняя статья блога  будет целиком посвящена работе с Excel Range или, говоря другими словами — работе с диапазонами ячеек Excel.

Про работу с этими объектами я уже вкратце говорил, а сегодня хотел бы поделиться с вами более полной информацией. Итак, начнем с самого простого.

1. Что такое Range и как его получить?

Согласно официальному определению Microsoft, Range :

представляет собой ячейки, строки, столбцы, набор ячеек, содержащих один или более смежных блоков ячеек, или 3-D диапазон.

Однако это определение не исключает того, что объектом Range может выступать и одна ячейка (Cell) листа.  Таким образом, чтобы получить в свое распоряжение объект Range, можно выполнить следующие операции c объектом Excel в Delphi:

var  MyRange: OLEVariat;
begin
{объект Range, состоящий из одной ячейки}
  MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1'];
{объект Range в виде строки из четырех ячеек}
  MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1:D1'];
{объект Range в виде столбца из четырех ячеек}
  MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1:A4'];
{объект Range в виде таблицы 4х4 ячейки}
  MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1:D4'];
end;

Если Вам неудобно в какой-либо ситуации использовать буквенные обозначение ячеек или Вы привыкли до этого момента иметь дело только с отдельными ячейками (Cells), то объект Range можно получить например вот так:

var  MyRange: OLEVariat;
     Cell_1, Cell2: OLEVariant;
begin
{получаем ссылку на объект Cells, соответствующей ячейке A1}
  Cell_1:=MyExcel.ActiveWorkBook.ActiveSheet.Cells[1,1];
{получаем ссылку на объект Cells, соответствующей ячейке C5}
  Cell_2:=MyExcel.ActiveWorkBook.ActiveSheet.Cells[5,3];
{получаем объект Range размером 3х5}
  MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range[Cell_1, Cell_2]
end;

Какой из способов Вы будете использовать в Delphi — не важно, так как результат будет один и тот же. Получив в свое распоряжение диапазон Вы можете использовать его далее, например, для того, чтобы установить вид его границ.

2. Свойства объекта Excel Range.

Рассмотрим основные свойства объекта Range и их применение работе в Excel в Delphi.

Вначале перечислю свойства, которые будут далее рассмотрены в статье.

Свойство Описание
Formula Возвращает или помещает в диапазон формулу
Value Возвращает или устанавливает значение для диапазона
Text возвращает текст из ячейки
Column Возвращает номер первого столбца в первой области, в указанном диапазоне
Columns возвращает объект Range, представляющий собой один столбец из всего диапазона
Comment Возвращает объект Comment для Range. В данном случае Range должен определять одну ячейку.
Address Возвращает реальный адрес диапазона Range

Formula

Возвращает или помещает в диапазон формулу.

Value

Возвращает или устанавливает значение для диапазона.Свойство Value замечательно тем, что с помощью него можно записать в ячейки абсолютно любые данные, особо не задумываясь о формате данных. Например, запишем в ячейки диапазона строку, число типа integer и число типа single: чтобы каждый раз не повторяться в листингах и не писать одни и те же элементы по 100 раз, будем считать, что в переменной Sheet уже содержится ссылка на активный лист (ActiveWorkSheet) активной книги (ActiveWorkBook) Excel (MyExcel)

var i:integer;
    s: single;
    str: string;
    Sheet: OLEVariant;
begin
  i:=100;
  s:=2.12;
  str:='Hello World!';
  Sheet.Range['A1'].Value:=i;
  Sheet.Range['A2'].Value:=s;
  Sheet.Range['A3'].Value:=str;
end;

Как видите, обращение к ячейкам было одно и то же и нигде я не приводил данный к какому-то типу — записал в ячейки всё как есть.

Если Вы хотите записать в весь диапазон Range одно и то же значение, то просто выполните:

Sheet.Range['A1:A10'].Value:=str;

и получите одну и ту же строку «Hello World!» в десяти ячейках Excel, но такие операции очень редко необходимы при работе с Excel в Delphi. Зато очень часто необходимо воспользоваться другой стороной свойства Value — прочитать большой объем данных из книги Excel за один прием и получить весь массив данных в Delphi. Операция чтения данных из Excel в Delphi более проста, чем Вам может показаться на первый взгляд. Проведем обратную операцию — прочитаем данные из Excel:

var  val: Variant;
     Sheet: OLEVariant;
     i:integer;
begin
  Val:=Sheet.Range['A1:A3'].Value;
  for i:=1 to 3 do
    ShowMessage(val[i,1]);
end;

Если Вам необходимо читать/писать большое количество данных, то наиболее быстрым способом работы с данными будет использование вариантных массивов. О том, как использовать вариантные массивы при работе с Excel в Delphi рассказывается в статье «Быстрая обработка данных Excel в Delphi.«

Как видите здесь мы за один прием прочитали данные сразу из трех ячеек Excel и отобразили их в сообщении. Этот прием чтения на порядок более скоростной, чем, например чтение содержимого каждой ячейки (Cells) в отдельности.

Text

Ещё одно простенькое свойство объекта Range — возвращает текст из ячейки. Самое главное отличие от свойства Value Text возвращает string только для чтения и использовать это свойство для чтения большого объема данных, как в предыдущем примере — ни в коем случае нельзя, так как переменная Val вернет значение Null.

Column

Возвращает номер первого столбца в первой области, в указанном диапазоне. Свойство только для чтения.

Чтобы продемонстрировать свойство в действии, давайте создадим такие диапазоны как показано на рисунке:

Excel_RangeТо есть каждый из диапазонов Range будет содержать по две несвязанные друг с другом области (Area). Причем первая область диапазона Range будет начинаться в столбце А, а первая область второго диапазона (Range 2) — в столбце B.

После того, как диапазоны будут созданы — посмотрим, что вернет нам свойство Column для каждого из диапазонов.

Листинг процедуры создания двух несвязных диапазонов Range следующий:

var Range1,Range2,BigRange: OLEVariant;
begin
{создаем первый диапазон}
  Range1:=Sheet.Range['A1:C4'];
  Range2:=Sheet.Range['E6:H9'];
  BigRange:=Sheet.Range[Range1,Range2];
  ShowMessage(IntToStr(BigRange.Column)); //показываем значение свойства
{создаем второй диапазон}
  Range1:=Sheet.Range['B7:C13'];
  Range2:=Sheet.Range['E1:H3'];
  BigRange:=Sheet.Range[Range1,Range2];
  ShowMessage(IntToStr(BigRange.Column)); //показываем значение свойства
end;

Так, в случае с первым Range Column вернет нам значение 1, а для второго Range — значение 2.

Columns

В отличие от предыдущего свойства, Columns возвращает не простое число, а объект Range, представляющий собой один столбец из всего диапазона.

Посмотрим как, например, используя это свойство можно изменять столбцы во всем диапазоне Range.

Для демонстрации воспользуемся предыдущим примером, изменим только окончание:

...
BigRange:=Sheet.Range[Range1,Range2];
for i:=1 to BigRange.Columns.Count do
  BigRange.Columns[i].Value:='Столбец №'+IntToStr(i);

Excel_Range_ColumnsВ итоге в каждый из столбцов диапазона должна записаться строка с номером этого столбца, результат представлен на рисунке. Как видите, в цикле все столбцы обработались «насквозь», хотя Range не содержал в себе столбец D.

Возвращает объект Comment для Range. В данном случае Range должен определять одну ячейку.

Для демонстрации свойства не будем заходить в Excel, поработаем с приложением прямо из Delphi. Для этого воспользуемся методом AddComment. То есть сначала запишем комментарий в ячейку, а потом прочитаем его используя свойство Comment:

Range2:=Sheet.Range['E6'];
{записали комментарий}
Range2.AddComment('Это комментарий ячейки области');
{прочитали}
ShowMessage(Range2.Comment.Text);

Address

Возвращает реальный адрес диапазона Range. Например для диапазона ячеек, заданных вот таким образом:

Range2:=Sheet.Range['A1:E6'];
Свойство Address будет содержать строку "$A$1:$E$6"

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

Помимо перечисленных выше свойств к объекту Range применимы все свойства, описанный в статье об изменении внешнего вида ячеек Excel, т.е. borders, color и пр.

3. Методы объекта Excel Range.

Теперь рассмотрим несколько полезных методов, которые могут Вам пригодиться при работе с Excel в Delphi.

Метод Описание
CheckSpelling Проверяет грамматику в выбранном диапазоне и при нахождении ошибок выводит окно для замены
PrintPreview Выводит на экран окно предварительного просмотра перед печатью выбранного диапазона ячеек
AutoFill Автозаполнение диапазона ячеек на основе данных из другого диапазона
AutoFit Изменяет ширину или высоту ячеек диапазона для наилучшего представления данных.
Clear Удаляет все данные из диапазона
ClearComments Удаляет все комментарии в диапазоне Range
ClearContents Удаляет все формулы из диапазона Range
ClearFormats Очищает форматы в диапазоне Range
ClearNotes Очищает все заметки в диапазоне Range
Copy Копирует содержимое диапазона Range в буфер обмена или в другой диапазон
PasteSpecial Специальная вставка диапазона
Cut Вырезает данные и при необходимости вставляет их в новый диапазон
Merge Объединение ячеек диапазона

CheckSpelling

Проверяет грамматику в выбранном диапазоне и при нахождении ошибок выводит окно для замены. Замечательной особенностью этого метода является то, что окно замены появляется даже при скрытом окне Excel, то есть, когда свойство Visible у объекта Excel равно false.

Вызывается метод без каких-либо дополнительных параметров:

Range['A1:H6'].CheckSpelling

PrintPreview

Выводит на экран окно предварительного просмотра перед печатью выбранного диапазона ячеек. Также как и предыдущий метод PrintPreview не имеет дополнительных параметров. Однако при попытке вызвать метод при скрытом окне Excel в лучшем случае возникнет исключительная ситуация, в худшем — зависание Вашего приложения. Так что перед вызовом:

не забывайте включить свойство Visible у Excel:

AutoFill

Автоматическое заполнение диапазона ячеек на основе данных из другого диапазона.

Вызов метода:

Range.AutoFill(Destination, Type)
Параметр Тип Описание
Destination Variant представляет собой объект Range уже заполненных ячеек. Эти ячейки должны входить в автозаполняемый диапазон
Type Integer тип автозаполнения (возможные значения см. ниже)

Рассмотрим применение метода на примере.

Sheet.Range['A1'].Value:=1;
Sheet.Range['A2'].Value:=2;
Source:=Sheet.Range['A1:A6'];
Range2:=Sheet.Range['A1:E6'];
Source.AutoFill(Range2, xlFillDefault)

В результате лист Excel примет следующий вид:

Excel_Range_AutoFillКак видите все столбцы второго диапазона Range заполнились значениями из диапазоны Source.

При использовании этого метода следует иметь в виду, что диапазон-источник (в нашем случае — это source) по одному из измерений должен совпадать с источником назначения. В приведенном примере совпадает размерность по строкам (в обоих диапазонах их шесть). Если размерности диапазонов не совпадают, то возникает исключительная ситуация.

При использовании метода мы использовали одну из констант в параметре Type xlFillDefault = 0. Используя её мы скопировали данные один-к-одному. Дополнительно Вы можете использовать следующие константы при автозаполнении:

Имя Значение Описание
xlFillDefault 0 скопировать данные один-к-одному
xlFillDays 5 копирование дней недели с расширением, т.е., если Вы запишете в ячейку слово «Понедельник» и попробуете провести автозаполнение ещё на 2 строки, то во второй и третьей строке появятся «Вторник» и «Среда»
xlFillCopy 1 копирует все данные и форматы, повторяя при необходимости
xlFillFormats 3 копирует только форматы источника
xlFillMonths 7 копирует названия месяцев. Работает аналогично xlFillDays
xlFillSeries 2 копирует данные с расширением, например 1,2,3 будут при копировании расширены до 4,5,6 и т.д. Также копирует форматы данных
xlFillValues 4 копирует только значения
xlFillWeekdays 6 копирует дни рабочей недели, работает аналогично xlFillDays, но только до пятницы
xlFillYears 8 копирует года. Работает аналогично xlFillDays
xlGrowthTrend 10 копирует числовые значения из источника, расширяя их в предположении, что каждое последующее число представляет собой предыдущее, но умноженное на некоторую величину. Например 1,2 раскопируются в 4, 8, 16 и т.д. Формат данных также копируется
xlLinearTrend 9 копирует числовые значения из источника, расширяя их в предположении, что каждое последующее число представляет собой предыдущее + некоторая величина. Например 1,2 раскопируются в 3, 4, 5 и т.д. Формат данных также копируется

AutoFit

Изменяет ширину или высоту ячеек диапазона для наилучшего представления данных.

Пример вызова:

Sheet.Range['A1'].Value:=1234567891012;
Sheet.Range['A2'].Value:=23456789;
Source:=Sheet.Range['A1:A2'];
Source.Columns.AutoFit

Приведет и изменению ширины столбца А таким образом, чтобы число было полностью видно на листе.

Методы очистки данных диапазона Range

Есть несколько различных методов очистки содержимого диапазона Range при работе с Excel в Delphi.

1. Clear

Удаляет все данные из диапазона.

Пример вызова:

2. ClearComments

Удаляет все комментарии в диапазоне Range.

Пример вызова:

3. ClearContents

Удаляет все формулы из диапазона Range

Пример вызова:

4. ClearFormats

Очищает форматы в диапазоне Range

Пример вызова:

5. ClearNotes

Очищает все заметки в диапазоне Range

Пример вызова:

Методы работы с буфером обмена Excel

1. Copy

Копирует содержимое диапазона Range в буфер обмена или в другой диапазон.

Попробуем реализовать работу метода следующим образом: заполним столбец А некоторыми значениями, а затем скопируем его сначала в буфер, а потом в столбец Е:

Sheet.Range['A1'].Value:=1;
Sheet.Range['A2'].Value:=2;
Sheet.Range['A3'].Value:=3;
Sheet.Range['A4'].Value:=4;
Sheet.Range['A5'].Value:=5;
Sheet.Range['A6'].Value:=6;
Source:=Sheet.Range['A1:A6'];
Source.Copy; //скопировали в буфер
Range2:=Sheet.Range['E1:E6'];
Source.Copy(Range2)//скопировали в новый диапазон

А для того, чтобы вставить данные из буфера обмена существует ещё один метод

2. PasteSpecial

Вызов метода:

MyRange.PasteSpecial(Paste, Operation, SkipBlanks, Transpose)
Параметр Тип Описание
Paste Integer определяет какая часть данных диапазона будет вставлена (возможные значения см. ниже)
Operation Integer операция, которая будет выполнена при вставке данных (возможные значения см. ниже)
SkipBlanks boolean True, для того чтобы пустые ячейки из буфера обмена не вставлялись в диапазон назначения. Значение по умолчанию False
Transpose boolean транспонирование столбцов и строк после вставки. По умолчанию устанавливается значение False

При определении параметра Paste следует использовать следующие константы:

Имя Значение Описание
xlPasteAll -4104 Вставка всех данных
xlPasteAllExceptBorders 7 Вставка всего содержимого за исключением вида границ диапазона
xlPasteAllUsingSourceTheme 13 Вставка всего содержимого, используя тему оформления источника
xlPasteColumnWidths 8 Копирует ширину столбцов
xlPasteComments -4144 Вставка комментариев
xlPasteFormats -4122 Вставка форматов данных
xlPasteFormulas -4123 Вставка формул
xlPasteFormulasAndNumberFormats 11 Вставка формул и чисел
xlPasteValidation 6 Вставка проверок
xlPasteValues -4163 Вставка значений
xlPasteValuesAndNumberFormats 12 Вставка значений и чисел

При использовании параметра Operation следует использовать следующие константы:

Имя Значение Описание
xlPasteSpecialOperationAdd 2 К скопированным данным будут добавлены значения из целевых ячеек
xlPasteSpecialOperationDivide 5 Скопированные данные будут разделены на значения в целевых ячейках
xlPasteSpecialOperationMultiply 4 Скопированные данные будут умножены на значения в целевых ячейках
xlPasteSpecialOperationNone -4142 При вставке значений никакие операции не будут применяться
xlPasteSpecialOperationSubtract 3 Из скопированных данных будут вычитаться значения целевых ячеек

Теперь рассмотрим применение метода на примере. В качестве исходных данных возьмем данные из предыдущего примера, но для вставки используем метод PasteSpecial:

...
Range2.PasteSpecial(Operation:=xlPasteSpecialOperationAdd);

После выполнения этой операции все данные вставятся в диапазон Range2, т.к. диапазон до вставки был пуст, то данные скопировались один-к-одному.

3. Cut

Вырезает данные и при необходимости вставляет их в новый диапазон. Метод работает аналогично методу Copy, но с последующим удалением данных из источника.

Merge

Объединение ячеек диапазона.

Для новичков, только начинающих постигать азы работы с excel в Delphi этот метод в какой-то момент становится камнем преткновения :). Дело в том, что очень часто возникает необходимость объединить часть ячеек листа для записи в объединенную область большого количества данных или длинной строки. В Excel есть такой метод Union, который и отвечает за объединение — им-то и пробуют пользоваться многие. А метод Union при вызове из Delphi применительно к диапазону Range вызывает исключительную ситуацию. Про Merge люди либо не знают, либо забывают. А использовать его достаточно просто:

Параметр Тип Описание
Across boolean True, чтобы объединить ячейки в каждой строке указанного диапазона как отдельные объекты. Значение по умолчанию False

Например, объединим ячейки в диапазоне Range2 из предыдущего примера:

После выполнения этой операции в диапазоне останется только верхнее значение, т.е. 1.

Дополнительные методы автозаполнения ячеек диапазона Range

В отдельную группу методов можно выделить 4 метода автозаполнения ячеек диапазона:

  1. FillDown
  2. FillUp
  3. FillRight
  4. FillLeft

Принцип действия этих методов один и тот же за исключением направления автозаполнения.

Например, давайте запишем в ячейку A1 какое-нибудь значение и воспользуемся методами FillDown и FillRight:

Sheet.Range['A1'].Value:=1;
Sheet.Range['A1:A6'].FillDown;
Sheet.Range['A1:F1'].FillRight;

После этого в строк 1 и столбце A появятся числа 1. Аналогичным образом можно заполнять не только строки или столбцы, но и таблицы целиком — достаточно знать в какую сторону двигаться при автозаполнениии и применять соответствующие методы Excel для Range.

Вот в принципе краткий обзор возможностей работы с объектов Range в Excel. В целом можно отметить, что работа с этим объектом в Delphi один из самых эффективных способов организации пресылки/получения данных между Excel и Delphi, не считая работы через XML формат.

Книжная полка

Название:Разработка приложений Microsoft Office 2007 в Delphi

Описание Описаны общие подходы к программированию приложений MS Office. Даны программные методы реализации функций MS Excel, MS Word, MS Access и MS Outlook в среде Delphi.

купить книгу delphi на ЛитРес

0
0
голоса

Рейтинг статьи

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Содержание

  1. Excel в Delphi
  2. Работа с Excel в Delphi. Основы основ
  3. Excel в Delphi. Методы объекта WorkSheet (лист)
  4. Диаграммы Excel в Delphi. Общие сведения
  5. Excel в Delphi. Работа с объектом Range (диапазон)
  6. Excel в Delphi. Как изменить внешний вид ячеек?
  7. Полная автоматизация. Редактируем объекты Excel, содержащиеся в документах Word и наоборот
  8. Быстрая обработка данных Excel в Delphi
  9. Пост-ответ. Работа с примечаниями в Excel
  10. Excel в Delphi. Работа со свойствами документа
  11. Создаем свои контролы на ленте Microsoft Office
  12. Камасутра с объектами Worksheet и Range в Excel
  13. How can I access a range of rows in Excel?
  14. 3 Answers 3
  15. Excel в Delphi. Свойства объекта WorkSheet.
  16. 1. Свойство Cells
  17. 2. Свойство Columns
  18. 3. Свойство Name
  19. 4. Свойство Range
  20. 5. Свойство Rows
  21. 6. Свойства StandartHeight и StandartWidth
  22. 7. Свойство UsedRange
  23. 8. Свойство Comments
  24. Excel в Delphi. Работа с объектом Range (диапазон)
  25. 1. Что такое Range и как его получить?
  26. 2. Свойства объекта Excel Range.
  27. Formula
  28. Value
  29. Column
  30. Columns
  31. Comment
  32. Address
  33. 3. Методы объекта Excel Range.
  34. CheckSpelling
  35. PrintPreview
  36. AutoFill
  37. AutoFit
  38. Методы очистки данных диапазона Range
  39. Методы работы с буфером обмена Excel
  40. Merge

Excel в Delphi

На этой странице представлена подборка статей, посвященные работе с Excel в Delphi. Все статьи расположены по порядку изложения материала в блоге, то есть от самых основ работы с Excel в Delphi до выполнения сложных операций, вставки диаграмм в книгу и так далее.

Работа с Excel в Delphi. Основы основ

Это вводная статья по работе с excel в delphi, которая поможет вам разобраться с основами по взаимодействия вашего приложения Delphi и Microsoft Excel. В статье рассмотрены следующие вопросы:

Excel в Delphi. Методы объекта WorkSheet (лист)

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

Диаграммы Excel в Delphi. Общие сведения

В статье рассмотрен способ добавления диаграммы в лист Excel через объект ChartObjects. План статьи следующий:

Excel в Delphi. Работа с объектом Range (диапазон)

Статья дает более полное представление о том, как можно работать с данными листа Excel в Delphi, используя объект Range (диапазон). Рассмотрены свойства и методы объекта range. План статьи:

Excel в Delphi. Как изменить внешний вид ячеек?

В статье рассматриваются вопросы оформления ячеек таблицы Excel в Delphi:

Полная автоматизация. Редактируем объекты Excel, содержащиеся в документах Word и наоборот

В статье рассматривается достаточно нетривиальный вопрос: как добраться до диаграммы Excel, внедренной в документ Word и работать с ней в Delphi? Статья состоит из трех частей:

Быстрая обработка данных Excel в Delphi

Эта статья раскрывает вопросы, касающиеся быстрой обработки данных при работе с Excel в Delphi. Одной из проблем, с которой сталкиваются начинающие разработчики, является то, что рассмотренные в предыдущих статьях методы чтения/записи данных в Excel работают очень медленно при большом количестве данных. Статья позволяет понять то, как сократить время операций при работе с Excel в Delphi в несколько раз, используя вариантные массивы и библиотеку XLSReadWrite.

Пост-ответ. Работа с примечаниями в Excel

В статье рассматривается то, как, используя Delphi вставлять и редактировать примечания в ячейках Excel. План статьи:

Excel в Delphi. Работа со свойствами документа

Статья раскрывает вопросы изменения свойств документа Excel в Delphi и состоит из трех частей:

Создаем свои контролы на ленте Microsoft Office

Небольшая заметка о том, как получить доступ у ленте Excel и добавить на неё свою кнопку.

Эти и другие статьи, посвященные работе с Excel в Delphi вы всегда сможете найти в блоге по тегу Excel в Delphi

Источник

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

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

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

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

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

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

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

Источник

How can I access a range of rows in Excel?

I am trying to translate this Excel VBA code to Delphi:

However in the Excel2010 unit in Delphi _Worksheet.Rows is an object, not a function or an array object, I also can’t find any Items property or similar.

The compiler message is:

What is the correct translation of the VBA code?

How can I access a certain range of Rows in Excel?

3 Answers 3

With early binding, selecting A column (rows 3..5000) and using EntireRow , for example like this:

The thing is, if you work with Excel from Delphi using early binding (e.g. using CoExcelApplication.Create), you are working with the raw interfaces Excel exposes, rather than the variants you get working with late binding (using CreateOleObject(‘Excel.Application’)).

Both methods have their strengths, early binding is best for speed and taking advantage of Delphi’s type-checking and code completion, whereas late binding is useful for avoiding to have to specify all the arguments of the methods which have a lot of them.

In Excel_Tlb (or whatever your Excel import unit is called), maybe the best way to think of the Rows member of the _Worksheet interface is as a function which returns a dispinterface to an ExcelRange object. The members which return an ExcelRange interface take two arguments, which specify the top left and bottom right cells defining the range. So, one way to do something along the lines you’ve asked about is the following:

That’s how you get at and use the Item property you were wondering about.

Источник

Excel в Delphi. Свойства объекта WorkSheet.

Продолжаем копаться в листах Excel и изучать особенности работы с ними. Сегодня мы рассмотри свойства объекта WorkSheet.

Основными свойствами, которые Вы с большой вероятностью будете использовать в своей работе с Excel в Delphi являются:

  1. Cells — ячейки
  2. Columns — столбцы
  3. Name — название листа
  4. Range — диапазон ячеек
  5. Rows — сроки
  6. StandartHeight — высота строк «по умолчанию»
  7. StandartWidth — ширина столбцов «по умолчанию»
  8. UsedRange — задействованный диапазон ячеек
  9. Comments — комментарии

Всего у листа рабочей книги Excel насчитывается 54 различных свойств, но, как показвает практика, для использования Excel в Dephi достаточно 9-15 различных свойств листа, чтобы получить необходимый результат, обеспечивающий удобство и наглядность представляемых данных.

Рассмотрим применение указанных выше свойств листа Worksheet при работе с Excel в Delphi.

1. Свойство Cells

Возвращает объект диапазона, который представляет собой все ячейки на листе (а не только клетки, которые используются в настоящее время)

Однако использование свойства Cells возможно (и более практично) в другом виде. Вы можете указать сразу за словом Cells номера столбца и строки и, таким образом, получить доступ к конкретной ячейке листа. Например, забегая немного вперед, посмотрим, как можно удалить формулы из ячейки:

Выполнив такую операцию Вы удалите из ячейки С5 формулу (или любое другое содержимое). Как можно заметить, в отличие от работы с таблицами StringGrid Delphi, здесь вначале указывается номер строки, а затем номер столбца (у String Grid напротив — сначала указывается номер столбца, а затем номер строки).

Аналогичным образом Вы можете записать в ячейку данные:

Ну, а чтобы показать использование этого свойства на примере, давайте напишем простенькую процедуру копирования таблицы StringGrid в любую область листа Worksheet Excel.

Здесь в качестве параметров процедуры необходимо задать: номера первого столбца (FirstCol) и первой строки (FirstRow) на листе Excel, начиная с которых необходимо копировать данные из таблицы StringGrid (Grid). Заметьте, что вызов свойств Cells у StringGrid и WorkSheet происходит по-разному (см. индексы ячейки).

Естественно, что представленная процедура может с легкостью применяться при копировании небольших таблиц. Однако при импорте больших объемов данных в Excel эта процедура будет очень сильно тормозить работы основной программы. Более скоростной способ импорта данных из Excel в Delphi и наоборот мы рассмотрим позже, при рассмотрении объекта Range.

2. Свойство Columns

Возвращает объект диапазона, который представляет собой все столбцы на активном листе. Если активный документ не является листом (а, например, диаграммой), то вызов свойства Columns приводит к исключительной ситуации.

Это свойство удобно использовать при редактировании столбцов данных. Например вот так:

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

3. Свойство Name

С этим свойством мы уже сталкивались. Свойство Name возвращает имя рабочего листа книги.

Так мы изменили имя текущего активного листа Excel. Кроме этого, свойство Name удобо использовать при поиске необходимого листа.

4. Свойство Range

Возвращает объект, представляющий собой одну ячейку или диапазон ячеек.

Так, мы получили доступ к ячейке А1 и записали в неё текст. А следующий фрагмент кода демонстрирует быстрый способ экспорта данных из листа Excel в Delphi:

Так, мы за одно обращение к листу считали столбец данных и в дальнейшем, не используя напрямую объект WorkSheet, обработали все данные в вариантном массиве Numbers. Этот способ чтения данных с листа Excel является наиболее скоростным из всех известных мне в настоящее время. Если знаете способ более скоростной — буду очень рад, если поделитесь им.

5. Свойство Rows

Это свойство аналогично свойству Columns, но в отличие от него, возвращает объект, представляющий собой строку листа. С помощью этого свойства Вы также можете изменять внешний вид ячеек листа, изменять шрифт и т.д.

6. Свойства StandartHeight и StandartWidth

Эти свойства позволяют получить значения высоты и ширины ячеек листа «по умолчанию». Свойство доступно только для чтения, т.е. вызов:

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

7. Свойство UsedRange

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

и далее, используя эти данные быстро прочитать весь занятый диапазон в вариантный массив для дальнейшей обработки.

Это свойство возвращает коллекцию комментариев на листе.

удалит второй комментарий из коллекции, а:

добавит в ячейку е5 новый комментарий.

Итак, резюмируем. Сегодня мы узнали: как записать данные в ячейку листа Excel, как прочитать данные из диапазона ячеек, как скопировать таблицу из Delphi в Excel, как быстро экспортировать данные из Excel в Delphi.

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

Источник

Excel в Delphi. Работа с объектом Range (диапазон)

Сегодняшняя статья блога будет целиком посвящена работе с Excel Range или, говоря другими словами — работе с диапазонами ячеек Excel.

Про работу с этими объектами я уже вкратце говорил, а сегодня хотел бы поделиться с вами более полной информацией. Итак, начнем с самого простого.

1. Что такое Range и как его получить?

Согласно официальному определению Microsoft, Range :

представляет собой ячейки, строки, столбцы, набор ячеек, содержащих один или более смежных блоков ячеек, или 3-D диапазон.

Однако это определение не исключает того, что объектом Range может выступать и одна ячейка (Cell) листа. Таким образом, чтобы получить в свое распоряжение объект Range, можно выполнить следующие операции c объектом Excel в Delphi:

Если Вам неудобно в какой-либо ситуации использовать буквенные обозначение ячеек или Вы привыкли до этого момента иметь дело только с отдельными ячейками (Cells), то объект Range можно получить например вот так:

Какой из способов Вы будете использовать в Delphi — не важно, так как результат будет один и тот же. Получив в свое распоряжение диапазон Вы можете использовать его далее, например, для того, чтобы установить вид его границ.

2. Свойства объекта Excel Range.

Рассмотрим основные свойства объекта Range и их применение работе в Excel в Delphi.

Вначале перечислю свойства, которые будут далее рассмотрены в статье.

Свойство Описание
Formula Возвращает или помещает в диапазон формулу
Value Возвращает или устанавливает значение для диапазона
Text возвращает текст из ячейки
Column Возвращает номер первого столбца в первой области, в указанном диапазоне
Columns возвращает объект Range, представляющий собой один столбец из всего диапазона
Comment Возвращает объект Comment для Range. В данном случае Range должен определять одну ячейку.
Address Возвращает реальный адрес диапазона Range

Formula

Возвращает или помещает в диапазон формулу.

Value

Возвращает или устанавливает значение для диапазона.Свойство Value замечательно тем, что с помощью него можно записать в ячейки абсолютно любые данные, особо не задумываясь о формате данных. Например, запишем в ячейки диапазона строку, число типа integer и число типа single: чтобы каждый раз не повторяться в листингах и не писать одни и те же элементы по 100 раз, будем считать, что в переменной Sheet уже содержится ссылка на активный лист (ActiveWorkSheet) активной книги (ActiveWorkBook) Excel (MyExcel)

Как видите, обращение к ячейкам было одно и то же и нигде я не приводил данный к какому-то типу — записал в ячейки всё как есть.

Если Вы хотите записать в весь диапазон Range одно и то же значение, то просто выполните:

и получите одну и ту же строку «Hello World!» в десяти ячейках Excel, но такие операции очень редко необходимы при работе с Excel в Delphi. Зато очень часто необходимо воспользоваться другой стороной свойства Value — прочитать большой объем данных из книги Excel за один прием и получить весь массив данных в Delphi. Операция чтения данных из Excel в Delphi более проста, чем Вам может показаться на первый взгляд. Проведем обратную операцию — прочитаем данные из Excel:

Как видите здесь мы за один прием прочитали данные сразу из трех ячеек Excel и отобразили их в сообщении. Этот прием чтения на порядок более скоростной, чем, например чтение содержимого каждой ячейки (Cells) в отдельности.

Ещё одно простенькое свойство объекта Range — возвращает текст из ячейки. Самое главное отличие от свойства Value Text возвращает string только для чтения и использовать это свойство для чтения большого объема данных, как в предыдущем примере — ни в коем случае нельзя, так как переменная Val вернет значение Null.

Column

Возвращает номер первого столбца в первой области, в указанном диапазоне. Свойство только для чтения.

Чтобы продемонстрировать свойство в действии, давайте создадим такие диапазоны как показано на рисунке:

То есть каждый из диапазонов Range будет содержать по две несвязанные друг с другом области (Area). Причем первая область диапазона Range будет начинаться в столбце А, а первая область второго диапазона (Range 2) — в столбце B.

После того, как диапазоны будут созданы — посмотрим, что вернет нам свойство Column для каждого из диапазонов.

Листинг процедуры создания двух несвязных диапазонов Range следующий:

Так, в случае с первым Range Column вернет нам значение 1, а для второго Range — значение 2.

Columns

В отличие от предыдущего свойства, Columns возвращает не простое число, а объект Range, представляющий собой один столбец из всего диапазона.

Посмотрим как, например, используя это свойство можно изменять столбцы во всем диапазоне Range.

Для демонстрации воспользуемся предыдущим примером, изменим только окончание:

В итоге в каждый из столбцов диапазона должна записаться строка с номером этого столбца, результат представлен на рисунке. Как видите, в цикле все столбцы обработались «насквозь», хотя Range не содержал в себе столбец D.

Возвращает объект Comment для Range. В данном случае Range должен определять одну ячейку.

Для демонстрации свойства не будем заходить в Excel, поработаем с приложением прямо из Delphi. Для этого воспользуемся методом AddComment. То есть сначала запишем комментарий в ячейку, а потом прочитаем его используя свойство Comment:

Address

Возвращает реальный адрес диапазона Range. Например для диапазона ячеек, заданных вот таким образом:

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

Помимо перечисленных выше свойств к объекту Range применимы все свойства, описанный в статье об изменении внешнего вида ячеек Excel, т.е. borders, color и пр.

3. Методы объекта Excel Range.

Теперь рассмотрим несколько полезных методов, которые могут Вам пригодиться при работе с Excel в Delphi.

Метод Описание
CheckSpelling Проверяет грамматику в выбранном диапазоне и при нахождении ошибок выводит окно для замены
PrintPreview Выводит на экран окно предварительного просмотра перед печатью выбранного диапазона ячеек
AutoFill Автозаполнение диапазона ячеек на основе данных из другого диапазона
AutoFit Изменяет ширину или высоту ячеек диапазона для наилучшего представления данных.
Clear Удаляет все данные из диапазона
ClearComments Удаляет все комментарии в диапазоне Range
ClearContents Удаляет все формулы из диапазона Range
ClearFormats Очищает форматы в диапазоне Range
ClearNotes Очищает все заметки в диапазоне Range
Copy Копирует содержимое диапазона Range в буфер обмена или в другой диапазон
PasteSpecial Специальная вставка диапазона
Cut Вырезает данные и при необходимости вставляет их в новый диапазон
Merge Объединение ячеек диапазона

CheckSpelling

Проверяет грамматику в выбранном диапазоне и при нахождении ошибок выводит окно для замены. Замечательной особенностью этого метода является то, что окно замены появляется даже при скрытом окне Excel, то есть, когда свойство Visible у объекта Excel равно false.

Вызывается метод без каких-либо дополнительных параметров:

PrintPreview

Выводит на экран окно предварительного просмотра перед печатью выбранного диапазона ячеек. Также как и предыдущий метод PrintPreview не имеет дополнительных параметров. Однако при попытке вызвать метод при скрытом окне Excel в лучшем случае возникнет исключительная ситуация, в худшем — зависание Вашего приложения. Так что перед вызовом:

не забывайте включить свойство Visible у Excel:

AutoFill

Автоматическое заполнение диапазона ячеек на основе данных из другого диапазона.

Параметр Тип Описание
Destination Variant представляет собой объект Range уже заполненных ячеек. Эти ячейки должны входить в автозаполняемый диапазон
Type Integer тип автозаполнения (возможные значения см. ниже)

Рассмотрим применение метода на примере.

В результате лист Excel примет следующий вид:

Как видите все столбцы второго диапазона Range заполнились значениями из диапазоны Source.

При использовании этого метода следует иметь в виду, что диапазон-источник (в нашем случае — это source) по одному из измерений должен совпадать с источником назначения. В приведенном примере совпадает размерность по строкам (в обоих диапазонах их шесть). Если размерности диапазонов не совпадают, то возникает исключительная ситуация.

При использовании метода мы использовали одну из констант в параметре TypexlFillDefault = 0. Используя её мы скопировали данные один-к-одному. Дополнительно Вы можете использовать следующие константы при автозаполнении:

Имя Значение Описание
xlFillDefault 0 скопировать данные один-к-одному
xlFillDays 5 копирование дней недели с расширением, т.е., если Вы запишете в ячейку слово «Понедельник» и попробуете провести автозаполнение ещё на 2 строки, то во второй и третьей строке появятся «Вторник» и «Среда»
xlFillCopy 1 копирует все данные и форматы, повторяя при необходимости
xlFillFormats 3 копирует только форматы источника
xlFillMonths 7 копирует названия месяцев. Работает аналогично xlFillDays
xlFillSeries 2 копирует данные с расширением, например 1,2,3 будут при копировании расширены до 4,5,6 и т.д. Также копирует форматы данных
xlFillValues 4 копирует только значения
xlFillWeekdays 6 копирует дни рабочей недели, работает аналогично xlFillDays, но только до пятницы
xlFillYears 8 копирует года. Работает аналогично xlFillDays
xlGrowthTrend 10 копирует числовые значения из источника, расширяя их в предположении, что каждое последующее число представляет собой предыдущее, но умноженное на некоторую величину. Например 1,2 раскопируются в 4, 8, 16 и т.д. Формат данных также копируется
xlLinearTrend 9 копирует числовые значения из источника, расширяя их в предположении, что каждое последующее число представляет собой предыдущее + некоторая величина. Например 1,2 раскопируются в 3, 4, 5 и т.д. Формат данных также копируется

AutoFit

Изменяет ширину или высоту ячеек диапазона для наилучшего представления данных.

Приведет и изменению ширины столбца А таким образом, чтобы число было полностью видно на листе.

Методы очистки данных диапазона Range

Есть несколько различных методов очистки содержимого диапазона Range при работе с Excel в Delphi.

1. Clear

Удаляет все данные из диапазона.

2. ClearComments

Удаляет все комментарии в диапазоне Range.

3. ClearContents

Удаляет все формулы из диапазона Range

4. ClearFormats

Очищает форматы в диапазоне Range

5. ClearNotes

Очищает все заметки в диапазоне Range

Методы работы с буфером обмена Excel

1. Copy

Копирует содержимое диапазона Range в буфер обмена или в другой диапазон.

Попробуем реализовать работу метода следующим образом: заполним столбец А некоторыми значениями, а затем скопируем его сначала в буфер, а потом в столбец Е:

А для того, чтобы вставить данные из буфера обмена существует ещё один метод

2. PasteSpecial

Параметр Тип Описание
Paste Integer определяет какая часть данных диапазона будет вставлена (возможные значения см. ниже)
Operation Integer операция, которая будет выполнена при вставке данных (возможные значения см. ниже)
SkipBlanks boolean True, для того чтобы пустые ячейки из буфера обмена не вставлялись в диапазон назначения. Значение по умолчанию False
Transpose boolean транспонирование столбцов и строк после вставки. По умолчанию устанавливается значение False

При определении параметра Paste следует использовать следующие константы:

Имя Значение Описание
xlPasteAll -4104 Вставка всех данных
xlPasteAllExceptBorders 7 Вставка всего содержимого за исключением вида границ диапазона
xlPasteAllUsingSourceTheme 13 Вставка всего содержимого, используя тему оформления источника
xlPasteColumnWidths 8 Копирует ширину столбцов
xlPasteComments -4144 Вставка комментариев
xlPasteFormats -4122 Вставка форматов данных
xlPasteFormulas -4123 Вставка формул
xlPasteFormulasAndNumberFormats 11 Вставка формул и чисел
xlPasteValidation 6 Вставка проверок
xlPasteValues -4163 Вставка значений
xlPasteValuesAndNumberFormats 12 Вставка значений и чисел

При использовании параметра Operation следует использовать следующие константы:

Имя Значение Описание
xlPasteSpecialOperationAdd 2 К скопированным данным будут добавлены значения из целевых ячеек
xlPasteSpecialOperationDivide 5 Скопированные данные будут разделены на значения в целевых ячейках
xlPasteSpecialOperationMultiply 4 Скопированные данные будут умножены на значения в целевых ячейках
xlPasteSpecialOperationNone -4142 При вставке значений никакие операции не будут применяться
xlPasteSpecialOperationSubtract 3 Из скопированных данных будут вычитаться значения целевых ячеек

Теперь рассмотрим применение метода на примере. В качестве исходных данных возьмем данные из предыдущего примера, но для вставки используем метод PasteSpecial:

После выполнения этой операции все данные вставятся в диапазон Range2, т.к. диапазон до вставки был пуст, то данные скопировались один-к-одному.

3. Cut

Вырезает данные и при необходимости вставляет их в новый диапазон. Метод работает аналогично методу Copy, но с последующим удалением данных из источника.

Merge

Объединение ячеек диапазона.

Для новичков, только начинающих постигать азы работы с excel в Delphi этот метод в какой-то момент становится камнем преткновения :). Дело в том, что очень часто возникает необходимость объединить часть ячеек листа для записи в объединенную область большого количества данных или длинной строки. В Excel есть такой метод Union, который и отвечает за объединение — им-то и пробуют пользоваться многие. А метод Union при вызове из Delphi применительно к диапазону Range вызывает исключительную ситуацию. Про Merge люди либо не знают, либо забывают. А использовать его достаточно просто:

Источник

The thing is, if you work with Excel from Delphi using early binding (e.g. using CoExcelApplication.Create), you
are working with the raw interfaces Excel exposes, rather than the variants
you get working with late binding (using CreateOleObject(‘Excel.Application’)).

Both methods have their strengths, early binding is best for speed and taking advantage
of Delphi’s type-checking and code completion, whereas late binding is useful
for avoiding to have to specify all the arguments of the methods which have
a lot of them.

In Excel_Tlb (or whatever your Excel import unit is called), maybe the best way to think
of the Rows member of the _Worksheet interface is as a function which returns
a dispinterface to an ExcelRange object. The members which return an ExcelRange interface take two arguments, which specify the top left and bottom right cells defining the range. So, one way to do something along the lines you’ve
asked about is the following:

That’s how you get at and use the Item property you were wondering about.

procedure TDefaultForm.TestRange;
var
  WB : _Workbook;
  vWB,
  vRange,
  vSheet : OleVariant;
  Sheet: _Worksheet;
  Range :  ExcelRange;
begin
  lcid := LOCALE_USER_DEFAULT;
  Excel := CoExcelApplication.Create;
  Excel.Visible[LOCALE_USER_DEFAULT] := True;

  WB := Excel.Workbooks.Add(EmptyParam, LOCALE_USER_DEFAULT);
  Sheet := WB.ActiveSheet as ExcelWorksheet;

  Range := Sheet.Range['A1', 'B2'];
  Range.RowHeight := 33;

  Range.Item[1, 1]  := 'some text long enough to wrap';
  Range.Item[1, 1].WrapText := True;

  Range.Item[1, 2]  := 'more text long enough to wrap';
  Range.Item[2, 2]  := 'other text long enough to wrap';


  //  The following shows how to use the special syntax for passing arguments
  //  in late-binding

  Excel.DisplayAlerts[LOCALE_USER_DEFAULT] := False;  //  suppresses  "Overwrite?" prompt if file already exists
  vWB := WB;
  //  Compare the following with what you would need if you called WB.SaveAs()
  vWB.SaveAs(FileName := ExtractFilePath(Application.ExeName) + 'Test.Xlsx');

  //  some things using late binding
  vSheet := Sheet;
  vRange := vSheet.Range['c3'];
  vRange.Value := 'some value';

  vRange := vSheet.Range['d3:e4'];
  vRange.Value := 'another value';

  //  retrieve the ExcelRange object from the vRange variant
  Range := IDispatch(vRange) as ExcelRange;

end;

Overview

This article is a little different for me as it’s not about the Open Tools API but about looking at the performance of code to export the contents of a VirtualStringTree out to Excel (which is a function of one of my applications, XER Tools 2.0e). I will not go into what the application does other than say it presents views of data using a VirtualStringTree component and I use it to export these views of data out to Excel for further processing.

The first thing I noticed was a difference in performance between by Development machine at home (about a year old) and my work machine (about 5-6 years ago) and surprisingly, my machine at home was significantly slower even though it’s supposed to by a rock ship compared to my old machine from work. Now my theory for that is to do with the bitness of Microsoft Office (which the application automates). On my home machine the application is 32-bits and Office is 64-bits but at work both are 32-bits. Its a theory not fact and I will not be able to check this until I have a fully function 64-bit version of my application.

This sparked me off to go and hunt down the lines of code that were causing the issue – I did this through simply commenting out lines of code until I had identified the suspect elements (I also used some method tracing in CodeSite for relative timings). The line of code that was causing the slow performance was the following for getting a range of cells.

R := SH.Range[
 SH.Cells.Item[iRow, 1],
 SH.Cells.Item[iRow, iColumns]
];

This call was being made (see below) for each row in the output, in order to format the output (copying of data is done by the clipboard). Similar code was also being used within sub-methods so I thought to see if creating the range once and using it many times would improve the performance of the code.

Refactoring and Caching

One thing I’ll say now, which I mentioned in a Webinar recently, is that when I look back at code, even 2-3 years ago, I sometimes cringe and wonder why it wasn’t better – so be kind to me about the following 🙂

Existing Code

The existing code below is used to format the output information in Excel: font changes; indentation, outlining, background colours, etc.

Procedure TXTExportToExcel.IndentWBSActs(Const SH: _Worksheet; Const setOptions : TXTExcelExportOptions);

Const
  iStartRow = 1;
  strProgressingFmt = 'Formatting...';
  iUpdateInterval = 10;
  iLowWBSLevel = 2;
  iHighWBSLevel = 8;

Var
  iWBSActIDCol : Integer;
  iRow : Integer;
  iWBSLevel : Integer;
  iRows, iColumns : Integer;
  R : ExcelRange;
  i : Integer;
  
Begin
  iWBSActIDCol := FindMainColumn();
  SH.Outline.AutomaticStyles := False;
  SH.Outline.SummaryRow := xlAbove;
  SH.Outline.SummaryColumn := xlLeft;
  iRows := SH.UsedRange[GetUserDefaultLCID].Rows.Count;
  iColumns := SH.UsedRange[GetUserDefaultLCID].Columns.Count;
  For iRow := iStartRow To iRows Do
    If iRow - iStartRow < FExcelText.Count Then
      Begin
        iWBSLevel := FExcelText.Indent[iRow - iStartRow];
        If eeoIndentForWBS In setOptions Then
          SH.Cells.Item[iRow, iWBSActIDCol].IndentLevel := Abs(iWBSLevel);
        R := SH.Range[
          SH.Cells.Item[iRow, 1],
          SH.Cells.Item[iRow, iColumns]
        ];
        For i := iLowWBSLevel To Min(Abs(iWBSLevel), iHighWBSLevel) Do //FI:W528
          R.Rows.Group(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
        FormatColours(iRow, iWBSLevel, iStartRow, R, setOptions);
        If FExcelText.FontStyles[iRow - iStartRow] <> [] Then
          Begin
            R.Font.Bold := fsBold In FExcelText.FontStyles[iRow - iStartRow];
            R.Font.Italic := fsItalic In FExcelText.FontStyles[iRow - iStartRow];
            R.Font.Underline := fsUnderline In FExcelText.FontStyles[iRow - iStartRow];
            R.Font.Strikethrough := fsStrikeOut In FExcelText.FontStyles[iRow - iStartRow];
          End;
        If iRow Mod iUpdateInterval = 0 Then
          FProgressMgr.Update(1, iRow, strProgressingFmt);
      End;
  FProgressMgr.Information('');
End;

New Code

What I decided to do was cache the range for the row at class level and reuse this where necessary. The below is the same method as above (renamed) and refactored. IndentRow() is a new method containing the code for creating and caching the range along with indentation and outlining code. FormatFontStyle() just encapsulates the existing code to update the range’s font styles.

procedure TXTExportToExcel.FormatData(const SH: _Worksheet);

Const
  iStartRow = 1;
  strProgressingFmt = 'Formatting...';
  iUpdateInterval = 10;

Var
  iWBSActIDCol : Integer;
  iRow : Integer;
  iWBSLevel : Integer;
  iRows : Integer;
  
  
Begin
  iWBSActIDCol := FindMainColumn();
  SH.Outline.AutomaticStyles := False;
  SH.Outline.SummaryRow := xlAbove;
  SH.Outline.SummaryColumn := xlLeft;
  FColumns := SH.UsedRange[GetUserDefaultLCID].Columns.Count;
  iRows := SH.UsedRange[GetUserDefaultLCID].Rows.Count;
  For iRow := iStartRow To iRows Do
    If iRow - iStartRow < FExcelText.Count Then
      Begin
        iWBSLevel := FExcelText.Indent[iRow - iStartRow];
        IndentRow(SH, iRow, iWBSActIDCol, iWBSLevel);
        OutlineRow(SH, iRow, iWBSLevel);
        FormatColours(SH, iRow, iWBSLevel, iStartRow);
        FormatFontStyles(SH, iRow, iStartRow);
        If iRow Mod iUpdateInterval = 0 Then
          FProgressMgr.Update(1, iRow, strProgressingFmt);
      End;
  FProgressMgr.Information('');
End;

Below is the refactored FormatFontStyle() method which now uses a new method, RowRange(), to get the range of cells to format. RowRange() will be explained below.

Procedure TXTExportToExcel.FormatFontStyles(Const SH: _Worksheet; Const iRow, iStartRow: Integer);

Begin
  {$IFDEF CODESITE}CodeSite.TraceMethod(Self, 'FormatFontStyles', tmoTiming);{$ENDIF}
  If FExcelText.FontStyles[iRow - iStartRow] <> [] Then
    Begin
      RowRange(SH, iRow).Font.Bold := fsBold In FExcelText.FontStyles[iRow - iStartRow];
      RowRange(SH, iRow).Font.Italic := fsItalic In FExcelText.FontStyles[iRow - iStartRow];
      RowRange(SH, iRow).Font.Underline := fsUnderline In FExcelText.FontStyles[iRow - iStartRow];
      RowRange(SH, iRow).Font.Strikethrough := fsStrikeOut In FExcelText.FontStyles[iRow - iStartRow];
    End;
End;

Below is the OutLineRow() method also using the new RowRange() method.

Procedure TXTExportToExcel.OutlineRow(Const SH: _Worksheet; Const iRow, iWBSLevel: Integer);

Const
  iLowWBSLevel = 2;
  iHighWBSLevel = 8;

Var
  i: Integer;
  
Begin
  If eeoOutlineWBS In FExcelExportOptions Then
    For i := iLowWBSLevel To Min(Abs(iWBSLevel), iHighWBSLevel) Do //FI:W528
      RowRange(SH, iRow).Rows.Group(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
End;

Caching

Now to the RowRange() method and the caching of the row information. In order to do this, the following fields were declared in the class.

FLastRangeRow : Integer;
FLastRowRange : ExcelRange;

These are initialised in the constructor.

Constructor TXTExportToExcel.Create(Const AOwner : TForm; Const XTServices : IXTServices);

Begin
  ... 
  FLastRangeRow := 0;
  FLastRowRange := Nil;
End;

Now for the RowRange() caching method. Below the code checks to see if the row being requested is different from the previous row used to generate the last row range. If it’s different, then the new range is created and returned else the old range is returned.

Function TXTExportToExcel.RowRange(Const SH : _Worksheet; Const iRow: Integer): ExcelRange;

Begin
  {$IFDEF CODESITE}CodeSite.TraceMethod(Self, 'RowRange', tmoTiming);{$ENDIF}
  If iRow <> FLastRangeRow Then
    Begin
      FLastRowRange := SH.Range[SH.Cells.Item[iRow, 1], SH.Cells.Item[iRow, FColumns]];
      FLastRangeRow := iRow;
    End;
  Result := FLastRowRange;
End;

After all of the above, there was a small improvement in the performance but nothing like I wanted. The reason for this is that a range is being requested for every row whether it’s required by the following code so I needed to think about how this can be improved.

Batching Similar Rows

So I thought about what was being done and felt that if I could identify contiguous rows that have same settings (Indent and Outline) then I could cut down the number of calls to RowRange() required by the code. When I finished the above refactoring I left in the code the following comment and code:

//: @todo The following 2 lines cound do with being process on batches of similar indentations
IndentRow(SH, iRow, iWBSActIDCol, iWBSLevel);
OutlineRow(SH, iRow, iWBSLevel);

The below code now checks for the change in indentation (which defined the data hierarchy) and applies the Indentation and Outlining to ranges of the same indentation.

Procedure TXTExportToExcel.FormatData();

Const
  strProgressingFmt = 'Formatting...';
  iUpdateInterval = 10;

Var
  iWBSActIDCol : Integer;
  iWBSLevel : Integer;
  iRows : Integer;
  iStartRow : Integer;
  iRow : Integer;
  iLastWBSLevel : Integer;
  
Begin
  {$IFDEF CODESITE}CodeSite.TraceMethod(Self, 'IndentWBSActs', tmoTiming);{$ENDIF}
  iWBSActIDCol := FindMainColumn();
  FWorksheet.Outline.AutomaticStyles := False;
  FWorksheet.Outline.SummaryRow := xlAbove;
  FWorksheet.Outline.SummaryColumn := xlLeft;
  FColumns := FWorksheet.UsedRange[GetUserDefaultLCID].Columns.Count;
  iRows := FWorksheet.UsedRange[GetUserDefaultLCID].Rows.Count;
  iRow := FStartRow;
  iStartRow := iRow;
  iLastWBSLevel := 0;
  While iRow <= iRows Do
    Begin
      iWBSLevel := FExcelText.Indent[iRow - 1];
      If iWBSLevel <> iLastWBSLevel Then
        Begin
          IndentRow(iStartRow, iRow - 1, iWBSActIDCol, iLastWBSLevel);
          OutlineRow(iStartRow, iRow - 1, iLastWBSLevel);
          iStartRow:= iRow;
          iLastWBSLevel := iWBSLevel;
        End;
      FormatColours(iRow, iWBSLevel);
      FormatFontStyles(iRow);         
      If iRow Mod iUpdateInterval = 0 Then
        FProgressMgr.Update(1, iRow, strProgressingFmt);
      Inc(iRow);
    End;
  IndentRow(iStartRow, iRow - 1, iWBSActIDCol, iLastWBSLevel);
  OutlineRow(iStartRow, iRow - 1, iLastWBSLevel);
  FProgressMgr.Information('');
End;

The below function no longer uses the RowRange() function (that is still used elsewhere) but instead creates a range reference for multiple rows

Procedure TXTExportToExcel.IndentRow(Const iStartRow, iEndRow, iWBSActIDCol, iWBSLevel: Integer);

Begin
  If eeoIndentForWBS In FExcelExportOptions Then
    FWorksheet.Range[
      FWorksheet.Cells.Item[iStartRow, iWBSActIDCol],
      FWorksheet.Cells.Item[iEndRow, iWBSActIDCol]
    ].IndentLevel := Abs(iWBSLevel) - 1;
End;

The below function uses an updated RowRange() function which now takes 2 rows as parameters, the start and end rows of the range.

**)
Procedure TXTExportToExcel.OutlineRow(Const iStartRow, iEndRow, iWBSLevel: Integer);

Const
  iLowWBSLevel = 2;
  iHighWBSLevel = 8;

Var
  i: Integer;
  
Begin
  If eeoOutlineWBS In FExcelExportOptions Then
    For i := iLowWBSLevel To Min(Abs(iWBSLevel), iHighWBSLevel) Do //FI:W528
      RowRange(iStartRow, iEndRow).Rows.Group(EmptyParam, EmptyParam, EmptyParam, EmptyParam);
End;

The RowRange() function now looks like below.

Function TXTExportToExcel.RowRange(Const iStartRow, iEndRow: Integer): ExcelRange;

Begin
  {$IFDEF CODESITE}CodeSite.TraceMethod(Self, 'RowRange', tmoTiming);{$ENDIF}
  If iStartRow <> FLastRangeRow Then
    Begin
      FLastRowRange := FWorksheet.Range[
        FWorksheet.Cells.Item[iStartRow, 1],
        FWorksheet.Cells.Item[iEndRow, FColumns]
      ];
      FLastRangeRow := iStartRow;
    End;
  Result := FLastRowRange;
End;

After all of the above, the performance was significantly better. It would seem that the Range method in Excel is very time consuming compared to many other functions so if you see similar performance issues you might want to go and hunt them down to see it its related to Range.

regards
Dave.

Цитата
Сообщение от NewbieProg
Посмотреть сообщение

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

Составной диапазон можно собрать с помощью метода Excel.Application.Union(). Но если его части не являются смежными и с одинаковым количеством строк, то через свойство Excel.Range.Value этого диапазона получить данные в виде отдельного массива не получится. Придётся выполнять перебор элементов в коллекции Excel.Range.Areas. Каждый элемент этой коллекции — это диапазон типа Excel.Range. И данные надо будет читать отдельно из каждого такого элемента — через Excel.Range.Areas[i].Value.
Вот пример сборки составного диапазона. Здесь для отсечения строк заголовка таблицы MS Excel применяется метод Application.Intersect(). А для сборки составного диапазона, как говорилось выше, применяется вызов метода Application.Union():

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//Получение данных из отдельных столбцов таблицы на листе MS Excel.
procedure TForm1.Button1Click(Sender: TObject);
var
  exApp, exBook, exSh, exURng, exRng : Variant;
  Od : TOpenDialog;
begin
  //Диалог выбора файла.
  Od := OpenDialog1; //OpenDialog1 уже должен быть на форме.
  if Od.InitialDir = '' then
    Od.InitialDir := ExtractFilePath(ParamStr(0));
  if not Od.Execute then
    Exit;
  if not FileExists(Od.FileName) then
  begin
    MessageBox(Handle, 'Файл не найден. Действие отменено.',
      'Внимание!', MB_OK + MB_ICONWARNING + MB_APPLMODAL);
    Exit;
  end;
 
  //Попытка подключиться к корневому объекту MS Excel.
  try
    exApp := CreateOleObject('Excel.Application');
  except
    MessageBox(Handle, 'Не удалось запустить MS Excel. Действие отменено.',
      'Ошибка!', MB_OK + MB_ICONERROR + MB_APPLMODAL);
    Exit;
  end;
  //Делаем видимым окно MS Excel.
  exApp.Visible := True;
 
  //Открываем файл рабочей книги.
  exBook := exApp.WorkBooks.Open(FileName:=Od.FileName);
  //Получаем ссылку на интерфейс первого рабочего листа.
  exSh := exBook.Worksheets[1];
  //Получаем ссылку на интерфейс используемого диапазона.
  exURng := exSh.UsedRange;
  {Будем считать, что верхние 2 строки диапазона - это заголовок таблицы.
  Если в используемом диапазоне 2 или менее строк, то считаем, что данных нет.}
  if exURng.Rows.Count <= 2 then
  begin
    MessageBox(Handle, 'Нет данных в таблице на листе MS Excel. Действие отменено.',
      'Внимание!', MB_OK + MB_ICONWARNING + MB_APPLMODAL);
    Exit;
  end;
 
  {Исключаем из диапазона строки заголовка. Для этого применим метод Intersect
  объекта Excel.Application. Этот метод определяет диапазон, который является
  пересечением диапазонов-аргументов. Мы возьмём исходный диапазон exURng и
  возьмём этот же диапазон, но сдвинутый вниз на 2 строки. Таким образом,
  пересечение этих двух диапазонов даст нам новый диапазон, в котором будут
  исключены верхние 2 строки.}
  exURng := exApp.Intersect(exURng, exURng.Offset[2, 0]);
  {Теперь нам надо собрать новый диапазон из отдельных столбцов диапазона exURng.
  Для этого мы воспользуемся методом Union объекта Excel.Application. Этот метод
  позволяет получить новый диапазон, собранный (склеенный) из диапазнов-аргументов.
  Склеим, например, 1, 10 и 12 столбцы (это столбцы "А", "J", "L").}
  exRng := exApp.Union(exURng.Columns[1], exURng.Columns[10], exURng.Columns[12]);
 
  ShowMessage('Собран составной диапазон, состоящий из 1, 10, 12 столбцов таблицы MS Excel.'
    + #13#10'Адрес составного диапазона: ' + exRng.Address);
end;

Цитата
Сообщение от NewbieProg
Посмотреть сообщение

приходится отдельно по каждому столбцу читать и заносить.

Наверное так и придётся делать. Или можно написать функцию, которая будет собирать данные из нескольких диапазонов и записывать их в один массив.

Для чтения данных из несоставных диапазонов MS Excel могу предложить такую функцию:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
{Чтение данных с листа рабочей книги MS Excel и возвращение их в виде вариантного массива.
aRow, aCol - координаты верхней левой ячейки диапазона MS Excel.
aCntCols - количество столбцов диапазона.
Нижняя граница диапазона MS Excel определяется автоматически, исходя из размеров
используемого диапазона aExSh.UsedRange.}
function ExcelToArr(aExSh : Variant; const aRow, aCol, aCntCols : Integer) : Variant;
var
  exRng, exCell1, exCell2, V : Variant;
  Row : Integer;
begin
  VarClear(Result);
  if VarIsClear(aExSh) or (aRow < 1) or (aCol < 1) or (aCntCols < 1) then
    Exit;
  //Левая верхняя ячейка диапазона с данными на листе MS Excel.
  exCell1 := aExSh.Cells[aRow, aCol];
  {Определяем номер нижней строки в используемом диапазоне.
  Используемый диапазон - это прямоугольная область на листе MS Excel,
  которая охватывает все используемые ячейки. К используемым ячейкам
  относятся не только те ячейки, которые содержат данные, но и те,
  в которых изменено оформление или в которых записаны формулы.}
  Row := aExSh.UsedRange.Row + aExSh.UsedRange.Rows.Count - 1;
  //Если диапазон, где должны быть данные, пустой, то выходим.
  if Row < exCell1.Row then
    Exit;
  {Определяем положение правой нижней ячейки диапазона, в соответствии
  с количеством строк данных на листе MS Excel и в соответствии с количеством
  столбцов в StringGrid, исключая фиксированные столбцы.}
  exCell2 := exCell1.Offset[Row - exCell1.Row, aCntCols - 1];
  //Определяем диапазон и получаем его данные.
  V := aExSh.Range[exCell1, exCell2].Value;
  {Если диапазон состоит из нескольких ячеек, то Excel возвращает его данные
  в виде двумерного вариантного массива.}
  if VarIsArray(V) then
    Result := V
  {Если диапазон состоит из одной ячейки, то Excel возвращает его данные
  в виде отдельного значения типа Variant. В этом случае требуется самостоятельно
  создать вариантный массив и записать в него значение.}
  else
  begin
    Result := VarArrayCreate([1, 1, 1, 1], varVariant);
    Result[1, 1] := V;
  end;
end;

Добавлено через 8 минут
И ещё следует коснуться вопроса об определении типа прочитанных данных. Вот код функции, которая записывает в таблицу типа TStringGrid данные, прочитанные ранее из диапазона MS Excel. В функции проверяется тип данных каждого элемента массива и выполняется соответствующее преобразование к строковому виду.

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{Очистка таблицы типа TStringGrid и сброс её размеров.
Фиксированные строки процедура не очищает.}
procedure SgClear(aSg : TStringGrid);
var
  Row : Integer;
begin
  for Row := aSg.FixedRows to aSg.RowCount - 1 do
    aSg.Rows[Row].Clear;
  aSg.RowCount := aSg.FixedRows + 1;
end;
 
{Запись данных вариантного массива в таблицу типа TStringGrid.}
procedure VArrToSg(const aVArr : Variant; aSg : TStringGrid);
var
  i, j, Row, Col : Integer;
begin
  //Очистка StringGrid.
  SgClear(aSg);
  //Если данных нет - выходим.
  if VarIsClear(aVArr) then
    Exit;
 
  //Задаём количество строк и столбцов таблицы.
  aSg.RowCount := aSg.FixedRows + VarArrayHighBound(aVArr, 1);
  aSg.ColCount := aSg.FixedCols + VarArrayHighBound(aVArr, 2);
  //Копирование данных массива в ячейки таблицы.
  Row := aSg.FixedRows - 1;
  Col := aSg.FixedCols - 1;
  for i := 1 to VarArrayHighBound(aVArr, 1) do
    for j := 1 to VarArrayHighBound(aVArr, 2) do
      //Выполняем преобразование к строковому виду в зависимости от типа данных.
      case VarType(aVArr[i, j]) of
        varOleStr : aSg.Cells[Col + j, Row + i] := aVArr[i, j];
        varDouble : aSg.Cells[Col + j, Row + i] := FloatToStr(aVArr[i, j]);
        varDate   : aSg.Cells[Col + j, Row + i] := FormatDateTime('dd.mm.yyyy', aVArr[i, j]);
        varEmpty  : aSg.Cells[Col + j, Row + i] := '';
        else
          aSg.Cells[Col + j, Row + i] := '<?>'; //Данные незарегистрированного типа.
      end;
end;



1



Понравилась статья? Поделить с друзьями:
  • Deliver on your word
  • Delimiters in excel vba
  • Delimiter in excel vba
  • Delight word meaning a
  • Delight in your word