Заказные программы. Веб сайты и веб приложения. Среда разработки.

Печать из Delphi в Excel по шаблону

Прилагается исходный код компонента ExcelView и процедуры экспорта и печати по шаблону в Excel для Delphi 2010/XE/XE2.
В разделе "Загрузки" доступна полная версия с примерами использования

Зачем это нужно?

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

Поэтому вполне закономерно, что Excel широко используется и для разработки различных печатных форм. Хотя по своим возможностям он уступает специализированным генераторам отчетов, уже практически для всех типовых документов можно найти готовые Excel-шаблоны, от коммерческого предложения до товарно-транспортной накладной по форме 1-Т (http://blanker.ru/doc/38)

Что и как экспортировать?

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

В шаблоне могут использоваться в качестве переменных:

  • все published свойства формы-владельца
  • все компоненты, принадлежащие форме-владельцу, и их published свойства
  • все наборы данных (наследники TDataSet) обрабатываются специальным образом, в шаблон подставляются значения их полей

Готовые решения на основе шаблонов

Компоненты для вывода в Excel довольно легко найти в интернете, например:

Do It Yourself

Библиотеки компонентов всем хороши (кроме стоимости, конечно), но даже наличие исходных текстов не всегда спасает от возможных неприятностей. Проблема в том, что кода становится слишком много. Например, один из лучших (на мой взгляд) - FlexCelReport - это 916 Кб в 174 исходных файлах. Переносить этот проект на современные версии Delphi становится проблематично.

В таких случаях остается вариант написания процедуры или компонента самостоятельно. В качестве стартовой точки для ознакомления можно рекомендовать http://www.webdelphi.ru/2009/08/rabota-s-excel-v-delphi-osnovy-osnov/http://www.codenet.ru/progr/delphi/stat/export-to-excel.php и т.д.

Таблицы и поля в шаблонах

У всех компонентостроителей - свои правила записи переменных в шаблонах. В FlexCelReport - это именованные диапазоны с двумя подчеркиваниями "__MAIN__" и переменные в ячейках вида ##DataSetName##FieldName, в ARExcelReport - <#table:DataSetName> ... <#table> и т.д.

Мы хотим еще проще. Только FieldName и ничего лишнего.

А где задается DataSet?

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

Еще одно допущение - если мы хотим вывести все записи из DataSet-а в виде таблицы, ставим перед его именем звездочку (*tblOrders). Без звездочки будут выводиться только значения полей текущей записи.

шаблон ExcelView

Свойства и компоненты в шаблонах

Для вывода в шаблоне published свойства текущей формы запишем в ячейке имя этого свойства в квадратных скобках: [Caption], [Tag]. Свойства компонентов формы записываются с именем компонента через точку: [Memo1.Lines]. Поддерживаются (пока) свойства следующих типов: Integer, Int64, String, Double, Boolean, TStrings.

Реализация

Весь код находится в файле ExcelView.pas (7 Кб). Компонент TExcelView имеет единственное published свойство TemplateFileName - это имя файла шаблона. Метод Show открывает Excel и запускает процесс экспорта.

Можно и не устанавливать компонент в палитру, а сразу выполнить процедуру

procedure ShowExcelView(Owner: TComponent; FileName: TFileName);
...

Обратите внимание на параметр Owner! Это тот компонент (форма, датамодуль), чьи свойства, компоненты и датасеты будут экспортироваться в Excel.

Взаимодействие с Excel происходит через OLE:

Excel := CreateOleObject('Excel.Application');
...

Для вывода набора данных сначала формируется вариантный массив:

ArrayData := VarArrayCreate([1, DataSet.RecordCount, 1, FieldList.Count], varVariant);
y := 1;
while not DataSet.Eof do
begin
  z := 1;
  for i := 0 to FieldList.Count-1 do begin
    if FieldList[i]='' then ArrayData[y, z] :=''
    else  begin
      if DataSet.FieldByName(FieldList[i]).DataType=ftFloat then
      ArrayData[y, z] := DataSet.FieldByName(FieldList[i]).AsFloat
      else ArrayData[y, z] := DataSet.FieldByName(FieldList[i]).Value;
    end;
    inc(z);
  end;
  DataSet.Next;
  inc(y);
end;

А потом уже вставляется в нужное место:

Range.Value := ArrayData;
...

Для вывода свойств используется модуль Rtti:

c := TRttiContext.Create;
t:=c.GetType(cmp.ClassInfo);
p:=t.GetProperty(PropName);
if p <> nil then begin
  IsNumeric:=false;
  if p.PropertyType.ToString = 'TStrings' then PropValue:=p.GetValue(cmp).AsType.Text
  else if p.PropertyType.ToString = 'Boolean' then PropValue:=BoolToStr(p.GetValue(cmp).AsBoolean,true)
  else
  case p.PropertyType.TypeKind of
    tkInteger,
    tkInt64    :  begin
      PropValue := IntToStr(p.GetValue(cmp).AsInteger);
      IsNumeric:=true;
    end;
    tkString,
    tkUString,
    tkLString    : PropValue := p.GetValue(cmp).AsString;
    tkFloat      : begin
      PropValue := FloatToStr(p.GetValue(cmp).AsExtended);
      IsNumeric:=true;
    end;
    else PropValue := '';
  end;
  if IsNumeric and (Trim(Copy(st,1,i1-1))='') and (Trim(Copy(st,i2+1,length(st)))='') then
    Sheet.Cells[Row, Col]:=StrToFloat(PropValue)
  else Sheet.Cells[Row, Col]:=Copy(st,1,i1-1)+PropValue+Copy(st,i2+1,length(st));
end;
c.Free;

Из за использования модулей Rtti и TypInfo компонент не работает в Delphi младше 2010. Можно либо удалить эти ссылки и весь метод ProcessProperty, либо переписать его для младших версий Delphi (см. http://delphi7.org/lit/faq/1618.php)