Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Работа с отчетами Rave Report в режиме RunTime

Автор: Турушев Виталий

Введение

С выходом Delphi 7 мы стали свидетелями выхода нового генератора отчетов Rave Report Borland Edition от разработчиков фирмы Nevrona. Среда разработки Rave имеет довольно много новшеств, и в тоже время ряд ошибок и недочетов. Среди положительных качеств можно отметить: сохранение проекта отчета в файл и чтение его из файла, что позволяет удобно загрузить или сохранить необходимый проект отчета, и в дальнейшем работать с ним. Также есть набор компонентов для конвертирования отчета Rave в другие форматы (PDF, HTML, RTF, TEXT). Rave Report значительно облегчает разработку отчетов где используются базы данных, ведь выбор компонентов доступа к источникам данных весьма неплох. Среди недочетов отмечу: недоработку среды разработки отчетов Rave, где присутствует ряд серьезных ошибок. Также Nevrona предоставила довольно таки очень скудную справку для разработчика и не порадовала достаточным количеством примеров работы с отчетами Rave.

Причиной написания данной статьи стало отсутствие примеров и описания работы с отчетами Rave в режиме RunTime, что не позволяет пользователю более гибко работать с отчетом (например: изменение данных/оформления в отчете при наступлении определенного события в программе, а возможности Event Editor в Rave Report увы ограничены), что отталкивает пользователей от использования генератора отчетов Rave. Возможно, по этой причине или по причине недоработки генератора отчета часть пользователей возвращается к генератору отчетов Quick Report или к генераторам отчетов других разработчиков.

Исследование классов в проекте отчета Rave Report

Для работы с отчетами в RunTime потребуется, знание имен подключаемых модулей в раздел uses, создаваемого проекта. Ниже представлена таблица описания основных модулей, которые могут понадобятся для работы с отчетом Rave в RunTime. Примечание: Для разработчиков CLX приложений название модулей почти идентично, только в имени модуля предшествует символ «Q». Например: QRvCsBars. Все модули находятся в каталоге «\Rave5\Lib» куда установлена среда программирования Delphi 7.

Имя модуля Описание модуля
RvClass    Этот модуль содержит реализацию базовых классов
RvProj     В этом модуле набор классов реализующие собственно сам проект 
             отчета и отвечающих за работу с ним
RvCsDraw   Классы в этом модуле реализуют графические примитивы
RvCsBars   В этом модуле реализованы штриховые коды
RvCsStd    В этом модуле реализуются основные элементы оформления отчета
RvCsRpt,   Набор классов в данном модуле служит для вывода данных из 
RvCsData     баз данных или других источников данных

Примечание: В качестве файла отчета взят уже готовый демонстрационный пример отчета RaveDemo.rav, поставляемый с генератором отчета Rave Report, который находится в каталоге «\Rave5\Demos». Поэтому следует указать полный путь к файлу отчета или скопировать данный файл в каталог с текущим проектом.

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

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

Также могут, пригодиться знания по работе с технологией RTTI для извлечения наименований свойств, событий (методов) и другой информации из объектов (компонентов) отчета Rave Report. Можно и не изучать принципы работы с технологией RTTI, ведь наименование доступных свойств интересующего объекта можно увидеть в среде разработки Rave Report инспектора объектов.

И так, двигаемся далее. Для работы с отчетом в RunTime, конечно, необходимо знать из набора каких объектов состоит данный отчет и, от каких классов произошли эти объекты, ведь в справочной системе Rave Report о них нет ни слова. Для этого необходимо написать процедуру, которая поможет узнать, из каких объектов состоит отчет, а также поможет выяснить имена классов и объектов. Код данной процедуры представлен ниже:

procedure GetListObjects(ListClass: TStrings; ClassX: TComponent; TabStr:
  string; AddObjects: Boolean = False);
var
  I: Integer;
begin
  if (ClassX = nil) or (ListClass = nil) then
    EXIT;
  for I := 0 to ClassX.ComponentCount - 1 do
  begin
    if AddObjects then
      ListClass.AddObject(TabStr + ClassX.Components[I].Name +
        ' - ' + ClassX.Components[I].ClassName, ClassX.Components[I])
    else
      ListClass.Add(TabStr + ClassX.Components[I].Name +
        ' - ' + ClassX.Components[I].ClassName);
    if ClassX.Components[I].ComponentCount > 0 then
      GetListObjects(ListClass, ClassX.Components[I], TabStr + '..',
        AddObjects);
  end;
end;

Как видите, процедура достаточно проста. В цикле процедуры осуществляется проход по всем дочерним компонентам в компоненте «ClassX» и если дочерний компонент содержит в себе еще вложенные компоненты, то используется рекурсивный вызов процедуры GetListObjects. В результате чего будет получен список всех компонентов. Вполне вероятно, что захочется узнать всех предков исследуемого объекта, тогда можно воспользоваться приведенной ниже процедурой.

procedure GetListParentClassName(ListClass: TStrings;
  ClassX: TClass; ClearList: Boolean = True);
begin
  if (ClassX = nil) or (ListClass = nil) then
    EXIT;
  if ClearList then
    ListClass.Clear;
  while ClassX <> nil do
  begin
    ListClass.Add(ClassX.ClassName);
    ClassX := ClassX.ClassParent;
  end;
end;

Пример вызова процедуры GetListParentClassName:
GetListParentClassName(MemoInfo.Lines, RvProjectRTR.ClassType);
или
GetListParentClassName(MemoInfo.Lines, TRvProject);

Как было сказано выше, для доступа к проекту отчета необходим класс TRaveProjectManager. Чтобы получить к нему доступ, необходимо обратиться к свойству «ProjMan» класса TRvProject. Как известно в проекте отчета Rave Report может содержаться несколько отчетов. Работа с отчетом осуществляется через класс TRaveReport. Для доступа к текущему активному отчету нужно обратиться к свойству «ActiveReport» класса TRaveProjectManager.

Вот для свойства «ActiveReport» класса TRaveProjectManager и следует применить выше описанную процедуру GetListObjects для получения списка объектов из отчета. Для использования процедуры можно использовать, к примеру, такие строки кода:

...
ListObjects.Clear;
GetListObjects(ListObjects.Items, RvProjectRTR.ProjMan.ActiveReport, '', True);
...

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

Совет: Если известен некий класс, но неизвестно в каком модуле данный класс описан, то это можно выяснить с помощью поисковика файлов с поддержкой поиска текста внутри файлов. В поисковике задать маску для поиска «*.dcu» в папке «\Rave5\Lib», а в строке для поиска текста указать наименование класса, например: TRaveControl.

Исследование объектов средствами технологии RTTI

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

Примечание: В данной статье не будет достаточно подробно рассматриваться принцип работы с RTTI так как эта тема довольно-таки велика по объему. Будет рассмотрено только то, что поможет в дальнейшей работе. Более подробно с принципами работы технологии RTTI вы можете познакомиться в книге Стива Тейксейра и Ксавье Пачеко «DELPHI 5 Руководство разработчика. Том 2. Разработка компонентов и работа с базами данных». Процедуры, реализованные в этой статье, основаны на примерах из этой книги, и были несколько доработаны для работы с проектом, описываемым в данной статье.

Для работы с RTTI в раздел проекта uses необходимо подключить модуль TypInfo. Для извлечения информации RTTI обычно требуется две структуры: PTypeInfo и PTypeData. Ниже приведена процедура, которая выводит базовую информацию об интересующем объекте.

// Извлечение базовой информации об объекте
procedure GetListClassInfo(ListInfo: TStrings; ClassX: TObject; ClearList:
  Boolean = True);
var
  // В данные структуры записывается информация об RTTI объекта
  Class_PTI: PTypeInfo;
  Class_PTD: PTypeData;
begin
  if (ClassX = nil) or (ListInfo = nil) then
    EXIT;
  if ClearList then
    ListInfo.Clear;
  // Получение информации об RTTI объекта
  Class_PTI := ClassX.ClassInfo;
  Class_PTD := GetTypeData(Class_PTI);
  // Вывод базовой информации об объекте
  with ListInfo do
  begin
    Add('Базовая информация:');
    Add(Format('Имя класса:                 '#9'  %s',[Class_PTI.Name]));
    Add(Format('Тип класса:                 '#9'  %s',
     [GetEnumName(TypeInfo(TTypeKind),
        Integer(Class_PTI.Kind))]));
    Add(Format('Размер объекта:             '#9'  %d',[ClassX.InstanceSize]));
    Add(Format('Описан в модуле:            '#9'  %s',[Class_PTD.UnitName]));
    Add(Format('Всего доступно свойств:     %d', [Class_PTD.PropCount]));
    if ClassX is TRaveControl then
    begin
      Add('Родительский компонент:');
      Add(Format('Тип класса:     %s',
        [TRaveControl(ClassX).Parent.ClassName]));
      Add(Format('Имя компонента: %s', [TRaveControl(ClassX).Parent.Name]));
    end;
    Add('Генеалогическое дерево класса:');
    // Вывод информации о предках объекта
    GetListParentClassName(ListInfo, ClassX.ClassType, False);
  end;
end;

Примечание: Следует помнить, что в RTTI доступны только те свойства и методы, которые определены в секции published исследуемого объекта, т.е. те которые видны в инспекторе объ-ектов среды разработки Delphi. Свойства и методы необъявленные в секции published через технологию RTTI будут недоступны.

Следующая процедура выводит список наименований свойств, свойств-событий (методы) и тип свойств исследуемого объекта. Также данная процедура выводит текущие значения, присвоенные свойствам объекта. Для свойств типа tkClass (в этих свойствах храниться ссылка на некий объект) выводиться имя объекта, на который ссылается данное свойство. Если же это свойство не ссылается на объект, то будет выведено значение «NIL».

// Извлечение информации о наименовании свойств и событий объекта
procedure GetListProperty(ListPropertys: TStrings; ClassX: TObject; AddObjects:
  Boolean = False; ClearList: Boolean = True);
var
  PropList: PPropList;
  Class_PTI: PTypeInfo;
  Class_PTD: PTypeData;
  I, PropertyCount: Integer;
  S, StrVal: string;
  TmpObj: TObject;
begin
  if (ClassX = nil) or (ListPropertys = nil) then
    EXIT;
  if ClearList then
    ListPropertys.Clear;
  Class_PTI := ClassX.ClassInfo;
  Class_PTD := GetTypeData(Class_PTI);
  if Class_PTD.PropCount <> 0 then
  begin
    // Выделение памяти под структуры TPropInfo, в зависимости от количества свойств объекта
    GetMem(PropList, SizeOf(PPropInfo) * Class_PTD.PropCount);
    try
      // Заполнение PropList указателями на структуры TPropInfo
      GetPropInfos(ClassX.ClassInfo, PropList);
      for I := 0 to Class_PTD.PropCount - 1 do
        // Добавляются свойства не являющиеся событиями
        if not (PropList[I]^.PropType^.Kind = tkMethod) then
        begin
          // Извлечение текущего значения свойства
          if PropList[I]^.PropType^.Kind = tkClass then
          begin
            TmpObj := GetObjectProp(ClassX, PropList[I]^.Name);
            if TmpObj = nil then
              StrVal := 'NIL'
            else
              // Если у объекта есть предок TComponent,
              // то извлекается имя объекта иначе имя класса
              if TmpObj is TComponent then
                StrVal := TComponent(TmpObj).Name
              else
                StrVal := '(' + TmpObj.ClassName + ')';
          end
          else
            StrVal := GetPropValue(ClassX, PropList[I]^.Name);
          S := Format('%s: %s = %s', [PropList[I]^.Name,
            PropList[I]^.PropType^.Name, StrVal]);
          if AddObjects then
            ListPropertys.AddObject(S, TObject(PropList[I]^.PropType^))
          else
            ListPropertys.Add(S);
        end;
      // Поиск свойств-событий
      PropertyCount := GetPropList(ClassX.ClassInfo, [tkMethod], PropList);
      ListPropertys.Add('*** Свойства-события ***');
      // Добавляются свойства-события
      for i := 0 to PropertyCount - 1 do
      begin
        S := Format('%s: %s', [PropList[I]^.Name, PropList[I]^.PropType^.Name]);
        if AddObjects then
          ListPropertys.AddObject(S, TObject(PropList[I]^.PropType^))
        else
          ListPropertys.Add(S);
      end;
    finally
      // Освобождение ранее выделенной памяти
      FreeMem(PropList, SizeOf(PPropInfo) * Class_PTD.PropCount);
    end;
  end;
end;

Процедур GetListClassInfo и GetListProperty вполне достаточно, чтобы изучить необходимый объект. Но самой полезной является, конечно, процедура GetListProperty. Как видите процедура GetListProperty достаточно сложная по виду. Еще бы, ведь структуры PTypeInfo и особенно PTypeData довольно-таки «ветвистые» по своему строению. Понимание выше приведенных процедур осложняет еще и то, что Borland не документирует данную технологию так как, она может изменяться от одной версии Delphi к другой, вследствие чего это не может гарантировать работоспособность одного и того же кода в различных версиях Delphi.

Для просмотра результата работы приведенных выше двух можно применить следующие строки кода:

...
if ListObjects.Items.Objects[ListObjects.ItemIndex] <> nil then
begin
  GetListClassInfo(MemoInfo.Lines,
    ListObjects.Items.Objects[ListObjects.ItemIndex]);

  GetListProperty(ListProperty.Items,
    ListObjects.Items.Objects[ListObjects.ItemIndex], True);
end;
...

Но и это еще не все возможности технологии RTTI. Также есть возможность получить достаточно полную информацию о свойстве или методе исследуемого объекта. Для извлечения столь немаловажной информации следует воспользоваться приведенной ниже процедурой.

// Извлечение информации о свойствах и методах объекта

procedure GetPropertyInfo(ListInfo: TStrings; PTI: PTypeInfo; PTD: PTypeData;
  ClearList: Boolean = True);
type
  // Структура для извлечения информации из методов (свойства-события)
  PParamRec = ^TParamRec;
  TParamRec = packed record
    Flags: TParamFlags;
    ParamName: ShortStringBase;
    TypeName: ShortStringBase;
  end;
var
  I: Integer;
  S, S2: string;
  TeStr,
    RStr: ^ShortStringBase;
  ParamRec: PParamRec;
  // Базовая информация для всех свойств
  procedure Name_Info;
  begin
    ListInfo.Add(Format('Тип свойства:             %s', [PTI.Name]));
    ListInfo.Add(Format('Подтип свойства:       %s',
      [GetEnumName(TypeInfo(TTypeKind),
        Integer(PTI^.Kind))]));
  end;
  // Информация для целочисленных, множеств и перечисляемых типов свойств
  procedure Int_Info;
  begin
    ListInfo.Add(Format('Минимальное значение:  %d', [PTD^.MinValue]));
    ListInfo.Add(Format('Максимальное значение: %d', [PTD^.MaxValue]));
  end;

  // Информация для типов свойств с плавающей точкой
  procedure Float_Info;
  begin
    // Определение подтипа свойства
    case PTD^.FloatType of
      ftSingle: S := 'ftSingle';
      ftDouble: S := 'ftDouble';
      ftExtended: S := 'ftExtended';
      ftComp: S := 'ftComp';
      ftCurr: S := 'ftCurr';
    end;
    ListInfo.Add(Format('Подтип tkFloat:        %s', [S]));
    ListInfo.Add('Минимальное значение:  ' + FloatToStr(PTD^.MinInt64Value));
    ListInfo.Add('Максимальное значение: ' + FloatToStr(PTD^.MaxInt64Value));
  end;
  // Информация для свойства представленного как класс
  procedure Class_Info;
  begin
    ListInfo.Add(Format('Предок класса свойства:           %s',
      [PTD^.ParentInfo^.Name]));
    ListInfo.Add(Format('Доступно свойств у объекта:    %d', [PTD^.PropCount]));
    ListInfo.Add(Format('Описан в модуле:                       %s',
      [PTD^.UnitName]));
  end;
  // Информация для методов (свойства-события)
  procedure Method_Info;
  var
    J: Integer;
  begin
    // Определение типа метода
    case PTD^.MethodKind of
      mkProcedure: S := 'Procedure ';
      mkFunction: S := 'Function ';
      mkConstructor: S := 'Constructor ';
      mkDestructor: S := 'Destructor ';
      mkClassProcedure: S := 'ClassProcedure ';
      mkClassFunction: S := 'ClassFunction ';
      mkSafeProcedure: S := 'SafeProcedure ';
      mkSafeFunction: S := 'SafeFunction ';
    end;
    // Извлечение информации о передаваемом параметре
    ParamRec := @PTD^.ParamList;
    J := 1;
    ListInfo.Add(Format('Передаваемых параметров:        %d',
      [PTD^.ParamCount]));
    while J <= PTD^.ParamCount do
    begin
      if J = 1 then
        S := S + '(';
      // Определение метода передачи параметра
      if pfVar in ParamRec.Flags then
        S2 := 'var ';
      if pfConst in ParamRec.Flags then
        S2 := 'const ';
      if pfArray in ParamRec.Flags then
        S2 := 'array of ';
      if pfOut in ParamRec.Flags then
        S2 := 'out ';
      // Извлечение информации о типе передаваемого параметра
      TeStr := Pointer(Integer(@ParamRec^.ParamName) +
        Length(ParamRec^.ParamName) + 1);
      S := S + S2;
      S := Format('%s%s: %s', [S, ParamRec^.ParamName, TeStr^]);
      Inc(J);
      // Извлечение информации о следующем передаваемом параметре
      ParamRec := PParamRec(Integer(ParamRec) + SizeOf(TParamFlags) +
        (Length(ParamRec^.ParamName) + 1) + (Length(TeStr^) + 1));
      if J > PTD^.ParamCount then
        S := S + ')';
      // Если метод является функцией, то извлекается информация о возвращаемом параметре
      if PTD^.MethodKind = mkFunction then
      begin
        RStr := Pointer(ParamRec);
        S := Format('%s: %s;', [S, RStr^]);
      end
      else
        S := S + '; ';
    end;
    ListInfo.Add(S);
  end;
  // Вывод информации для строковых типов свойств
  procedure Str_Info;
  begin
    ListInfo.Add(Format('Максимальная длина:  %d', [PTD^.MaxLength]));
  end;

begin
  if (ListInfo = nil) or (PTI = nil) or (PTD = nil) then
    EXIT;
  if ClearList then
    ListInfo.Clear;
  Name_Info;
  // Извлечение информации для свойств множеств
  if PTI^.Kind = tkSet then
  begin
    PTI := PTD^.CompType^;
    PTD := GetTypeData(PTI);
    Name_Info;
  end;

  case PTI^.Kind of
    tkInteger: Int_Info;
    tkFloat: Float_Info;
    tkString: Str_Info;
    tkLString: Str_Info;
    tkWString: Str_Info;
    // Извлечение информации для перечисляемых свойств
    tkEnumeration:
      begin
        Int_Info;
        ListInfo.Add('Варианты значений:');
        for I := PTD^.MinValue to PTD^.MaxValue do
          ListInfo.Add(Format('Значение: %s', [GetEnumName(PTI, I)]));
      end;
    tkClass: Class_Info;
    tkMethod: Method_Info;
  end;
end;

Примечание: Для перечисляемых свойств, информацию «Минимальное значение» и «Максимальное значение» выводимую процедурой GetPropertyInfo следует понимать как первый и последний индекс элемента перечисляемого свойства.

Использовать процедуру GetPropertyInfo можно так:

procedure TFormRTR.ListPropertyClick(Sender: TObject);
var
  TI: PTypeInfo;
  TD: PTypeData;
begin
  if ListProperty.Items.Objects[ListProperty.ItemIndex] = nil then
    EXIT;
  TI := PTypeInfo(ListProperty.Items.Objects[ListProperty.ItemIndex]);
  TD := GetTypeData(TI);
  GetPropertyInfo(MemoInfo.Lines, TI, TD);
end;

Имея на вооружении такие замечательные процедуры, наконец, то можно изучить интересующие классы, узнать свойства и методы, а также тип свойств исследуемых классов. Получив достаточно подробную информацию с помощью технологии RTTI теперь, наконец, можно перейти к работе с проектом отчета Rave Report в режиме RunTime, но сначала ознакомимся с основными классами, которые встречаются в проекте отчета Rave Report.

Описание классов TRaveXXX

В этом разделе статьи содержится описание классов, из которых в основном состоит проект отчета. Проект отчета формируют три основных класса: TRaveProjectManager, TRaveReport, TRavePage. Для работы с источниками данных могут использоваться классы TRaveDataView, TRaveDataField, а так же TRaveRegion и TRaveDataBand. Разберем эти основные классы более конкретно.

Как описывалось выше, класс TRaveProjectManager обеспечивает всю базовую работу с проектом отчета Rave. Осуществляет такие основные задачи как: чтение/сохранение проекта отчета, работа с коллекцией отчетов и глобальными страницами, поиск необходимого отчета и компонентов TRaveXXX и многое другое. Ниже приведено описание основных свойств класса TRaveProjectManager.

Примечание: * – предположительное описание свойства, ввиду того, что в справочной системе Rave не предоставлена информация по данному свойству. (скрытый) – данное свойство доступно через технологию RTTI, но скрыто в инспекторе объектов среды разработки Rave Report.

TRaveProjectManager

  • AdminPassword Пароль для доступа к проекту отчета *
  • Categories Хранит список наименования категорий. Далее отдельному отчету можно указать тип категории, что помогает организовать более удобную работу и произвести поиск отчетов по категориям
  • CompileNeeded Необходима компиляция (скрытый) *
  • Description Сюда записывается более подробная информация о компоненте
  • DevLocked Блокировка компонента от случайных изменений его свойств
  • FullName Альтернативное наименование компонента
  • Locked Блокировка компонента от случайных изменений его свойств
  • Name Имя компонента
  • Parameters Описание параметров, которые могут использоваться для сохранения временных вычислений или другой информации
  • PIVars По назначению подобны Parameters, но присваиваются значения, которые определены после передачи команды на печать (After Print)
  • SecurityControl Определяет параметры доступа к серверам баз данных для ввода имени и пароля пользователя
  • Tag Тег, хранит целое число, которое используется разработчиком для собственных нужд
  • Units Определяет единицу измерения для всех отчетов
  • UnitsFactor Коэффициент для перевода текущей единицы измерения в дюймы
  • OnBeforeReport Обработчик события перед генерацией отчета
  • OnAfterReport Обработчик события после генерации отчета
  • OnBeforePrint Обработчик события пред посылкой задания на печать
  • OnAfterPrint Обработчик события после завершения печати

Примечание: Если вы исследовали, какой либо класс TRaveXXX средствами RTTI, то наверно обратили внимание, что события OnBeforeReport, OnAfterReport, OnBeforePrint, OnAfterPrint не являются методами как в VCL Delphi, а являются ссылками на класс TRaveSimpleEvent.

Теперь познакомимся с классом TRaveReport. Данный класс представляет собой отдельный отчет, который является контейнером, хранящим в себе страницы отчета. Описание основных свойств класса TRaveReport приведены ниже.

TRaveReport

  • AlwaysGenerate Перед печатью отчета заполняет переменные типа TotalPages, чтобы их значение было известно перед печатью первой страницы
  • Category Позволяет установить принадлежность отчета к заданной категории. Список доступных категорий задается в свойстве «Categories» менеджера отчетов TRaveProjectManager
  • Collate Определяет тип упорядочивания задания на печать
  • CompileNeeded Необходима компиляция (скрытый) *
  • Copies Хранит количество копий, после печати отчета
  • Description Сюда записывается более подробная информация о компоненте
  • DevLocked Блокировка компонента от случайных изменений его свойств
  • Duplex Установка типа дуплексной печати для принтера (не для всех принтеров)
  • FirstPage Первая страница отчета
  • FullName Альтернативное наименование компонента
  • Locked Блокировка компонента от случайных изменений его свойств
  • MaxPages Ограничивает число генерируемых страниц при генерации отчета после вызова метода Execute, 0 – генерируются все страницы
  • Name Имя компонента
  • PageList Список страниц для печати. Здесь можно задать какие страницы печатать и в каком порядке
  • Parameters Описание параметров, которые могут использоваться для сохранения временных вычислений или другой информации
  • PIVars По назначению подобны Parameters, но присваиваются значения, которые определены после передачи команды на печать (After Print)
  • Printer Задается имя принтера, на который выводится печать. Если поле пустое, то вывод данных осуществляется на текущий принтер
  • Resolution Установка качества печати
  • SecurityControl Определяет параметры доступа к серверам баз данных для ввода имени и пароля пользователя
  • Tag Тег, хранит целое число, которое используется разработчиком для собственных нужд
  • OnAfterPrint Обработчик события после завершения печати
  • OnAfterReport Обработчик события после генерации отчета
  • OnBeforePrint Обработчик события пред посылкой задания на печать
  • OnBeforeReport Обработчик события перед генерацией отчета

Переходим к классу TRavePage. Данный класс реализует страницу отчета и также является контейнером, в который помещаются различные элементы оформления отчета, а также вспомогательные не визуальные элементы, например как TRaveFontMaster. Рассмотрим свойства класса TRavePage в приведенной ниже таблице.

TRavePage

  • Bin Указывается тип лотка для подачи бумаги
  • BinCustom Если в представленном списке Bin нет необходимого типа лотка, то указывается пользовательская константа лотка, поддерживаемая принтером
  • CompileNeeded Необходима компиляция (скрытый) *
  • Description Сюда записывается более подробная информация о компоненте
  • DevLocked Блокировка компонента от случайных изменений его свойств
  • FullName Альтернативное наименование компонента
  • GotoMode Определяет метод перехода по страницам «GotoPage» при печати
  • GotoPage Печать указанной страницы после печати текущей страницы
  • GridLines Определяет шаг видимой линии в координатной сетке
  • GridSpacing Размер шага между линиями в координатной сетке
  • Locked Блокировка компонента от случайных изменений его свойств
  • Name Имя компонента
  • Orientation Вид ориентации страницы (книжная/альбомная)
  • PageHeight Высота страницы
  • PageWidth Ширина страницы
  • PaperSize Выбор формата страницы поддерживаемый текущим принтером
  • Parameters Описание параметров, которые могут использоваться для сохранения временных вычислений или другой информации
  • PIVars По назначению подобны Parameters, но присваиваются значения, которые определены после передачи команды на печать (After Print)
  • Tag Тег, хранит целое число, которое используется разработчиком для собственных нужд
  • WasteFit Запрещает или разрешает при генерации отчета, пропорционально располагать элементы оформления на всю рабочую область страницы
  • OnAfterPrint Обработчик события после завершения печати
  • OnAfterReport Обработчик события после генерации отчета
  • OnBeforePrint Обработчик события пред посылкой задания на печать
  • OnBeforeReport Обработчик события перед генерацией отчета

TRaveDataView – данный класс-посредник обеспечивает работу и связь между источниками данных и отчетом (данное назначение класса предположительно ввиду отсутствия справочной информации о нем). Описан он в модуле RvDirectDataView.

TRaveDataView

  • CompileNeeded Необходима компиляция *
  • ConnectionName Хранит имя подключенного источника данных
  • Description Сюда записывается более подробная информация о компоненте
  • DevLocked Блокировка компонента от случайных изменений его свойств
  • FullName Альтернативное наименование компонента
  • Locked Блокировка компонента от случайных изменений его свойств
  • Name Имя компонента
  • Tag Тег, хранит целое число, которое используется разработчиком для собственных нужд
  • OnBeforeReport Обработчик события перед генерацией отчета
  • OnAfterReport Обработчик события после генерации отчета
  • OnBeforePrint Обработчик события пред посылкой задания на печать
  • OnAfterPrint Обработчик события после завершения печати

TRaveDataField – данный класс представляет собой поле данных и предоставляет вывод информации из источника данных. Этот класс расположен в модуле RvDataField.

TRaveDataField

  • Calculated Вычисляемое поле или нет *
  • Description Сюда записывается более подробная информация о компоненте
  • DevLocked Блокировка компонента от случайных изменений его свойств
  • FieldName Имя поля
  • FullName Альтернативное наименование компонента
  • Locked Блокировка компонента от случайных изменений его свойств
  • Name Имя компонента
  • NullText Текст, выводимый по умолчанию (если нет данных в источнике данных)
  • Size Размер поля
  • Tag Тег, хранит целое число, которое используется разработчиком для собственных нужд
  • OnBeforeReport Обработчик события перед генерацией отчета
  • OnAfterReport Обработчик события после генерации отчета
  • OnBeforePrint Обработчик события пред посылкой задания на печать
  • OnAfterPrint Обработчик события после завершения печати

Ниже в таблице приведено описание классов, которые можно встретить в палитре компонентов среды разработки отчетов Rave Report. Визуальные компоненты – это те самые элементы оформления отчетов, такие как: линия, текст, штриховые коды и др. Не визуальные компоненты выполняют вспомогательные функции в оформлении отчета (TRaveFontMaster, TRavePageNumInit), или выполняют вычисления при генерации отчета, такие компоненты как TRaveCalcController, TRaveCalcOp и другие. Также к не визуальным элементам отчетов относятся компоненты TRaveRegion, TRaveBand, TRaveDataBand и компоненты доступа к источникам данных. В заголовке таблицы приведено имя модуля, где данные классы реализованы.

RvCsDraw

  • TRaveGraphicBase Является базовым классом для всех классов данного модуля. Произошел данный класс от TRaveControl.
  • TRaveSurface Происходит от класса TRaveGraphicBase и уже содержит в себе свойства реализующие базовые элементы оформления и стилей.
  • TRaveLine Элемент оформления – линия.
  • TRaveHLine Элемент оформления – горизонтальная линия.
  • TRaveVLine Элемент оформления – вертикальная линия
  • TRaveRectangle Элемент оформления – прямоугольник
  • TRaveSquare Элемент оформления – квадрат.
  • TRaveEllipse Элемент оформления – эллипс.
  • TRaveCircle Элемент оформления – окружность.

RvCsBars

  • TRaveBaseBarCode Базовый класс штрихового кода.
  • TRavePostNetBarCode Постсетевой штриховой код (PostNet), используется американской почтовой службой в доставке почты.
  • TRaveI2of5BarCode Числовой штриховой код (I2of5).
  • TRaveCode39BarCode Алфавитно-цифровой штриховой код (Code39). Символ может хранить кодируемые данные. Разработан, чтобы кодировать 26 прописных букв, 10 цифр и 7 специальных символов.
  • TRaveCode128BarCode Алфавитно-цифровой штриховой код с высокой плотностью (Code128). Символ может хранить кодируемые данные. Разработан, чтобы кодировать первые 128 ASCII символов.
  • TRaveUPCBarCode Универсальный штриховой код изделия (UPC).
  • TRaveEANBarCode Европейский международный номер, штриховой код подобен штриховому коду UPC (EAN).

RvCsStd

  • TRaveText Элемент оформления – текст.
  • TRaveMemo Элемент оформления – текстовое поле MEMO.
  • TRaveSection Вспомогательный элемент оформления, реализующий группировку объектов.
  • TRaveBitmap Элемент оформления для вывода растрового изображения.
  • TRaveMetaFile Элемент оформления для вывода метафайла.
  • TRaveFontMaster Вспомогательный элемент оформления, для установки свойства шрифта у текстовых компонентов. Компоненты позволяющие работать с компонентом TRaveFontMaster содержат свойство «FontMirror».
  • TRavePageNumInit Вспомогательный элемент оформления позволяющий производить нумерацию страниц.

RvCsRpt

  • TRaveRegion Компонент-контейнер, размещающий в себе элементы оформления отчета. Также позволяет создать печать отчета в несколько столбцов. Используется для работы с источниками баз данных.
  • TRaveBand Компонент-контейнер, также размещающий в себе элементы оформления отчета. В основном используется для создания верхних и нижних колонтитулов или других сносок.
  • TRaveDataBand Компонент-контейнер, размещающий в себе элементы оформления отчета для вывода информации из баз данных.
  • TRaveDataCycle Используется для вычислений, сортировки или фильтрации.

RvCsData

  • TRaveDataText Элемент оформления отчета, для вывода однострочных данных из источника баз данных.
  • TRaveDataMemo Элемент оформления отчета, для вывода многострочных данных из источника баз данных.
  • TRaveDataMirrorSection Компонент-контейнер, размещающий в себе элементы оформления отчета для доступа к источникам баз данных и объединяющий их в одну группу.
  • TRaveCalcText Элемент оформления отчета, позволяющий производить вычисления по указанному полю источника данных.
  • TRaveCalcOp Вспомогательный элемент, позволяющий производить вычисления по двум указанным полям источника данных.
  • TRaveCalcTotal Вспомогательный элемент, позволяющий производить вычисления по указанному полю источника данных и имеет возможность передать вычисленное значение какому либо элементу оформления отчета.
  • TRaveCalcController Вспомогательный элемент, позволяющий задать параметры вычислений. Также данный класс выполняет все заданные функции вычислений.

Доступ к объектам проекта отчета Rave Report в режиме RunTime

Ну вот, когда проведена большая работа по исследованию классов в проекте отчета Rave Report, наконец, можно приступить непосредственно к работе с отчетом в режиме RunTime. Еще раз напомню порядок доступа к активному (текущему) отчету Rave:

  1. Для доступа к проекту отчета необходимо обратиться к классу TRaveProjectManager.
  2. Для доступа к классу TRaveProjectManager следует обратиться к свойству «ProjMan» класса TRvProject.
  3. Чтобы получить доступ к активному отчету, следует обратиться к свойству «ActiveReport» класса TRaveProjectManager.
  4. То есть последовательность такого типа: TRvProject.ProjMan.ActiveReport.

Как вы уже знаете в отчете содержаться еще и страницы. К сожалению, разработчики не предоставили методов для навигации по страницам отчета (представлено только одно свойство «FirstPage» – первая страница), но это проблема вполне решаема. Ниже приведена процедура, которая возвращает список указателей на объекты (если таковые имеются), порожденных от указанного класса.

// Возвращает список указателей на объекты, порожденных от указанного класса FindClass
procedure GetObjectList(RootComponent: TComponent; FindClass: TClass; var
  PageList: TList);
var
  I: Integer;
begin
  PageList.Clear;
  if (RootComponent = nil) or (PageList = nil) then
    EXIT;
  for I := 0 to RootComponent.ComponentCount - 1 do
    if RootComponent.Components[I] is FindClass then
      PageList.Add(RootComponent.Components[I]);
end;

Пример вызова процедуры:

GetObjectList(RvProjectRTR.ProjMan.ActiveReport, TRavePage, RavePageList);

Данный вызов процедуры заполняет список RavePageList указателями на все найденные страницы в текущем отчете. Теперь используя список RavePageList можно удобно осуществлять навигацию по страницам отчета, точно так же, как по списку отчетов представленный менеджером отчетов TRaveProjectManager. Список отчетов можно получить, обратившись к свойству «ReportList» класса TRaveProjectManager. Используя процедуру GetObjectList можно получить список и других объектов произошедших от определенного класса, что облегчает навигацию по объектам определенного типа.

Примечание: У класса TRaveReport в наличии есть свойство «PageList». Данное свойство определяет порядок страниц при печати. Допустим, в отчете имеется 10 страниц, а в «PageList» указанно печатать 3-ю и 8-ю страницу. Тогда свойство «PageList.Count», будет равным 2-ум, и доступ вы сможете получить только к 3-й и 8-й странице отчета. Если же в свойстве «PageList» нет ссылок на страницы отчета (PageList = NIL), то при попытке обратиться к данному свойству будет получено сообщение об ошибке. Поэтому не следует забывать специфику данного свойства.

Примечание: TRaveReport также предоставляет свойство «Page» доступное только для чтения. Можно было бы предположить, что данное свойство хранит ссылку на активную страницу текущего отчета, но по каким то причинам в данном свойстве все время присутствует значение равное NIL. Так что назначение данного свойства для меня пока неизвестно.

Слов сказано уже много, но на практике еще мало чего сделано. Думаю, настало время перейти к практической части. За основу, как оговаривалось в начале статьи, взят демонстрационный проект отчета «RaveDemo.rav», вот над ним и будут производиться все опыты на практике. Для экспериментов возьмем, к примеру, отчет «Mirror Report».

Попробуем изменить заголовок первой страницы у отчета «Mirror Report». Для оттого необходимо:

  1. Найти и активизировать отчет «Mirror Report» средством вызова метода «SelectReport» класса TRvProject.
  2. Произвести поиск текстового элемента оформления TRaveText на первой странице отчета TRavePage. Для этого можно воспользоваться методом «FindRaveComponent» класса TRaveProjectManager, который в случае успешного поиска в качестве возвращаемого параметра вернет найденный объект, в противном случае возвратится NIL.
  3. Убедиться, что искомый объект произошел от нужного класса.
  4. Если искомый объект найден, то произвести с ним все необходимые манипуляции.
  5. После внесенных изменений сгенерировать отчет методом «Execute» или «ExecuteReport» класса TRvProject.

Ниже представлен пример реализации всего выше изложенного.

...
var
  I: Integer;
  TmpRaveComponent: TRaveComponent;
begin
  // Поиск и активизация необходимого отчета. Метод вернет false если отчет не найден
  if not RvProjectRTR.SelectReport('Mirror Report', true) then
    EXIT;
  // Поиск компонента с именем 'Text1' на первой странице отчета
  TmpRaveComponent := RvProjectRTR.ProjMan.FindRaveComponent('Text1',
    RvProjectRTR.ProjMan.ActiveReport.FirstPage);
  // Если объект найден, и он произошел от класса TRaveText
  if (TmpRaveComponent <> nil) and (TmpRaveComponent is TRaveText) then
  begin
    // Замена выводимого текста
    TRaveText(TmpRaveComponent).Text := 'Это мой новый заголовок';
    // Изменение стиля шрифта
    TRaveText(TmpRaveComponent).Font.Style := [fsItalic];
  end;
  // Генерация активного отчета
  RvProjectRTR.Execute;
end;
...

Как видите, ничего особо сложного нет. Найдя необходимый объект в отчете с ним можно делать почти все что угодно. Почему почти? Когда вы исследовали визуальные элементы оформления отчета средствами RTTI, то, может быть, обратили внимание на то, что все эти компоненты не имеют свойства «Visible», хотя данное свойство уже доступно начиная с класса TRaveComponent. К сожалению, разработчики по каким то причинам не стали учитывать значение свойства «Visible», что в принципе огорчает. Все-таки иногда может возникнуть необходимость скрыть, что-либо из отчета в зависимости от событий в программе.

Есть возможность имитировать свойство «Visible» используя свойство «Parent» нужного объекта. Чаще всего в качестве родителя объектов выступает страница отчета, но для группировки визуальных компонентов отчета в качестве родителя может выступать компонент-контейнер TRaveSection (также группировку объектов выполняют TRaveRegion и TRaveDataBand). Если визуальному компоненту не будет указан родитель в свойстве «Parent», т.е. значение равное NIL, то данный объект не будет отображен в сгенерированном отчете. Для имитации свойства «Visible» объектов вновь обратимся к отчету «Mirror Report». В данном отчете скроем все элементы оформления TRaveRectangle путем скрытия объектов TRaveSection. Последовательность действий будет следующей:

  1. Найти и активизировать отчет «Mirror Report» средством вызова метода «SelectReport» класса TRvProject.
  2. Пробежаться по всем дочерним компонентам страницы и найти компоненты производные от класса TRaveSection.
  3. У компонентов TRaveSection в свойстве «Parent» проверить наличие родителя. Если родительский компонент назначен, то данному свойству присвоить значение NIL (скрытие объекта), в ином случае данному свойству в качестве родителя присвоить страницу отчета (отображение объекта на странице).
  4. После внесенных изменений сгенерировать отчет.

Возможно, некоторые задались вопросом, – почему бы сразу не скрыть TRaveRectangle напрямую, а через объект TRaveSection? Все возможно, но в данном случае:

  1. Элементы оформления TRaveRectangle объединены в группу компонентом TRaveSection. Следовательно, у объектов TRaveRectangle общий родитель TRaveSection. При назначении свойству «Parent» значения NIL у объекта TRaveSection, данный объект теряет родителя и скрывается со всеми его дочерними компонентами TRaveRectangle.
  2. Если каждому объекту TRaveRectangle в свойстве «Parent» указать значение равное NIL, а затем в данном свойстве в качестве нового родителя указать страницу отчета, то данные компоненты окажутся не на своем месте. Это вызвано тем, что значения в свойствах «Left» и «Top» компонента TRaveRectangle получены относительно клиентской части компонента TRaveSection. Отсюда вывод, что для корректного восстановления объектов для каждого TRaveRectangle нужно указать своего прежнего родителя, а это несколько усложняет подход к данной задаче. Но при необходимости все это возможно реализовать.

Ниже представлен фрагмент кода, имитирующий скрытие объектов.

...
var
  I: Integer;
  TmpRaveComponent: TRaveComponent;
begin
  // Поиск и активизация необходимого отчета. Метод вернет false если отчет не найден
  if not RvProjectRTR.SelectReport('Mirror Report', true) then
    EXIT;
  // Проход по всем объектам первой страницы отчета
  for I := 0 to RvProjectRTR.ProjMan.ActiveReport.FirstPage.ComponentCount - 1
    do
    // Если объект является TRaveSection
    if RvProjectRTR.ProjMan.ActiveReport.FirstPage.Components[I] is TRaveSection
      then
    begin
      TmpRaveComponent :=
        TRaveComponent(RvProjectRTR.ProjMan.ActiveReport.FirstPage.Components[I]);
      // Если данный объект не имеет родителя, то в качестве
      // родителя указывается первая страница отчета
      if TmpRaveComponent.Parent = nil then
        TmpRaveComponent.Parent := RvProjectRTR.ProjMan.ActiveReport.FirstPage
          // Если объект имеет родителя, то уничтожается ссылка на родителя,
        // что в следствии приводит к скрытию объекта
      else
        TmpRaveComponent.Parent := nil;
    end;
  // Генерация активного отчета
  RvProjectRTR.Execute;
  ...

Примечание: Вместо свойства «Visible» разработчики предусмотрели свойство «DisplayOn» у визуальных элементов оформления. Данное свойство позволяет установить, в каком случае отображать данный элемент оформления. Отображать: только при предварительном просмотре, только при выводе на печать, отображать в обоих случаях или наследовать настройки родителя. Изменение значения данного свойства упорно игнорируется визуальными компонентами. Очень жаль.

Рассмотрим еще одну проблему при оформлении отчета. Следует обратить внимание на отсутствие привычных свойств у некоторых визуальных компонентов. Возьмем, например компонент для вывода текста (TRaveText – аналог компонента TLabel). Данный компонент не предоставляет свойства для выбора фонового цвета (цвет кисти Brush) и по умолчанию является прозрачным.

На заметку: Фоновый цвет TRaveText останется прозрачным в том случае, если сгенерировать отчет, вызвав метод «Execute» или «ExecuteReport» класса TRvProject. Если отчет сгенерировать из среды разработки Rave Report, то фоновая заливка белого цвета под текстом останется непрозрачной. Такая вот недоработка присутствует в Rave Report.

Также в данном компоненте отсутствует свойство, определяющее его высоту. Не найдется там и привычное свойство «AutoSize» для выравнивания клиентского размера объекта под размер выводимого текста. Отчасти некоторые недостатки можно имитировать. Для придания фонового цвета можно подложить под компонент, к примеру, элемент оформления прямоугольник (TRaveRectangle).

Создать имитацию фонового цвета для текста в редакторе событий «Event Editor» весьма проблематично. Данная проблема выражается в следующем: т.к. в TRaveText не предоставлено свойство «Height» определяющее его высоту, то соответственно нет возможности получить точный размер по высоте компонента. Следовательно, при изменении размера шрифта компонента придется вручную подгонять высоту компонента TRaveRectangle. Также попытка получить доступ к свойству «BoundsRect, Height» (или к другому свойству или методу, не предоставленному в инспекторе объектов среды разработки отчета Rave Report) компонента может привести к плачевным результатам (даже если компиляция кода в «Event Editor» прошла успешно). В лучшем случае можно отделаться сообщением об ошибке, в худшем – критическое завершение работы генератора отчета Rave Report c потерей всех несохраненных данных, а при работе под

Windows 98 возможен полный «крах» системы.

Как видите, редактор событий «Event Editor» не предоставляет таких гибких возможностей для работы, как доступ к объектам в режиме RunTime. Для имитации фонового цвета как упоминалось выше вполне можно применить TRaveRectangle. Для этого необходимо:

  1. Создать объект производный от TRaveRectangle и придать ему нужное оформление.
  2. Подогнать размеры TRaveRectangle под размеры TRaveText.
  3. Поместить объект TRaveRectangle на задний план, воспользовавшись методом объекта «SendToBack».

Реализация имитации фонового цвета под текстом представлена ниже. В данном случае не удалось только создать имитацию «AutoSize» для объекта TRaveText:

...
var
  I: Integer;
  TmpRaveComponent: TRaveComponent;
  TmpRavePage: TRavePage;
  BGRect: TRaveRectangle;
begin
  // Поиск и активизация необходимого отчета. Метод вернет false если отчет не найден
  if not RvProjectRTR.SelectReport('Mirror Report', true) then
    EXIT;
  TmpRavePage := RvProjectRTR.ProjMan.ActiveReport.FirstPage;
  // Поиск компонента с именем 'Text1' на первой странице отчета
  TmpRaveComponent := RvProjectRTR.ProjMan.FindRaveComponent('Text1',
    TmpRavePage);
  // Если объект найден, и он произошел от класса TRaveText
  if (TmpRaveComponent <> nil) and (TmpRaveComponent is TRaveText) then
  begin
    // Создание графического примитива - прямоугольник
    BGRect := TRaveRectangle.Create(TmpRavePage);
    // Заполнение свойств вновь созданного объекта
    with BGRect do
    begin
      Name := 'BackGroundRect';
      FillColor := clYellow;
      BorderStyle := psClear;
      // Указание родителя компонента
      Parent := TmpRavePage;
      // Подгонка размеров прямоугольника под размеры компонента TRaveText
      BoundsRect := TRaveText(TmpRaveComponent).BoundsRect;
      // Перемещение объекта на задний план
      SendToBack;
    end;
  end;
  // Генерация активного отчета
  RvProjectRTR.Execute;
end;
...

В режиме RunTime вполне самостоятельно возможно создать: отчет, страницу отчета, визуальные элементы оформления или другие объекты, поддерживаемые отчетом Rave Report. Для создания нового отчета необходимо обратиться к методу «NewReport» менеджера отчетов TRaveProjectManager. В качестве возвращаемого параметра, возвращается объект порожденный от класса TRaveReport вновь созданного отчета. Для создания новой страницы для отчета следует обратиться к методу «NewPage» класса TRaveReport. Данный метод, как и в предыдущей ситуации, в качестве возвращаемого параметра возвращает объект типа TRavePage вновь созданной страницы отчета.

Примечание: При создании нового отчета автоматически создается и новая страница.

При создании многостраничного отчета не следует забывать про свойство «GotoPage» компонента TRavePage или свойство «PageList» компонента TRaveReport, ведь данные свойства содержат указатели на следующую генерируемую страницу отчета. Если не воспользоваться ни одним из представленных свойств, то при генерации отчета будет сгенерированна только первая страница отчета. При создании новой страницы или отчета в RunTime нет необходимости указывать вновь созданным объектам хозяина или родителя компонента, все это, данные методы «NewReport» и «NewPage» осуществляют самостоятельно. При создании визуальных и не визуальных компонентов следует указать хозяина объекта. Если в качестве хозяина будет передано значение равное NIL, то после завершения работы с данными объектами, программист должен уничтожить их самостоятельно, воспользовавшись методом «Free» данного объекта. Для всех визуальных элементов оформления отчета следует обязательно указывать нужного родителя в свойстве «Parent». Вспомните имитацию скрытия объектов

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

В ниже приведенном примере создается отчет с двумя страницами. На первой странице создается текстовый элемент оформления TRaveText, а на второй странице компонент

TRaveBitMap для вывода графического изображения.

const
  NewRep = 'MyNewReport'; // Имя отчета (динамически создаваемого)
  FieldStr = 'FieldStr'; // Имя строкового поля
  FieldInt = 'FieldInt'; // Имя целочисленного поля
  NewDataView = 'NewDataView';
  // Имя компонента для посредника с источником данных
  ...

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

function SetPointInPercent(RaveControlWH: TRaveUnits; Percent: Byte):
  TRaveUnits;
begin
  Result := (RaveControlWH / 100) * Percent;
end;

...
const
  Picture = '1.bmp';
var
  TmpRaveReport: TRaveReport;
  TmpRavePage: TRavePage;
  TmpRaveText: TRaveText;
  TmpRaveBitMap: TRaveBitmap;
  TmpPageList: TRaveComponentList;
begin
  // Если отчет уже создан, то выходим
  if RvProjectRTR.SelectReport(NewRep, true) then
    EXIT;
  // Добавление нового отчета в проект и заполнение его свойств
  TmpRaveReport := RvProjectRTR.ProjMan.NewReport;
  with TmpRaveReport do
  begin
    Name := NewRep;
    FullName := NewRep;
    // Создание новой страницы для отчета (здесь будет графическое изображение)
    TmpRavePage := NewPage;
    // Указывается следующая страница для генерации отчета,
    // после того, как будет сгенерированна первая страница отчета
    TmpRaveReport.FirstPage.GotoPage := TmpRavePage;
  end;
  // Создание компонента для вывода графического изображения и заполнение его свойств
  TmpRaveBitMap := TRaveBitMap.Create(TmpRavePage);
  with TmpRaveBitMap do
  begin
    Name := 'MyNewRaveBitMap';
    Left := SetPointInPercent(TmpRavePage.PageWidth, 5);
    Top := SetPointInPercent(TmpRavePage.PageHeight, 20);
    Width := SetPointInPercent(TmpRavePage.PageWidth, 30);
    Height := SetPointInPercent(TmpRavePage.PageHeight, 30);
    // Загрузка графического изображения (если найден указанный файл)
    if FileExists(Picture) then
      Image.LoadFromFile(Picture);
    // Размеры изображения будут подогнаны под клиентские размеры компонента
    MatchSide := msBoth;
    // Родителем данного компонента будет страница TmpRavePage
    Parent := TmpRavePage;
  end;
  // Создание элемента TRaveText и заполнение его свойств
  TmpRaveText := TRaveText.Create(TmpRaveReport.FirstPage);
  with TmpRaveText do
  begin
    Left := SetPointInPercent(TmpRaveReport.FirstPage.PageWidth, 10);
    ;
    Top := SetPointInPercent(TmpRaveReport.FirstPage.PageHeight, 15);
    ;
    Font.Size := 18;
    Name := 'MyNewRaveText';
    // Родителем данного объекта будет первая страница отчета
    Parent := TmpRaveReport.FirstPage;
    Text := 'Этот текст расположен на первой странице отчета';
  end;
  // Заполение свойств страницы TmpRavePage
  with TmpRavePage do
  begin
    Name := 'MyNewRavePage';
    FullName := 'Моя новая страница';
  end;
  // Создание списка генерируемых страниц
  TmpPageList := TRaveComponentList.Create;
  TmpPageList.Add(TmpRavePage);
  TmpPageList.Add(TmpRaveReport.FirstPage);
  TmpRaveReport.PageList := TmpPageList;
  // Обновление списка доступных отчетов
  RvProjectRTR.GetReportList(ListReport.Items, True);
end;
...

В данном примере для перехода к следующей генерируемой странице применены оба свойства «PageList» и «GotoPage». Чтобы лучше понять их принцип работы создайте новый отчет по выше представленному примеру и сгенерируйте его.

...
if RvProjectRTR.SelectReport('Мой новый отчет', true) then
  RvProjectRTR.Execute;
...

Как видите, отчет сгенерировал три страницы. Это происходит потому, что сначала осуществляется проход по списку в свойстве «PageList» (если он не пуст). Вновь генерируемая страница из этого списка также проверяет свое свойство «GotoPage» на наличие перехода на другую страницу. После генерации страницы указанной в свойстве «GotoPage» (если данное свойство указывает на страницу) продолжается обход по списку «PageList». Следует соблюдать осторожность при указании порядка генерации страниц отчета, при задании неверного порядка может произойти бесконечный цикл генерации страниц (если данный случай имеет место быть). Схематично это можно отобразить так (в скобках указан порядок генерации страниц):

Если очистить список страниц у отчета в свойстве «PageList» (все в том же выше приведенном примере) и вновь сгенерировать отчет, то результат соответственно будет совсем другим.

...
if RvProjectRTR.SelectReport('Мой новый отчет', true) then
begin
  RvProjectRTR.ProjMan.ActiveReport.PageList := nil;
  RvProjectRTR.Execute;
end;
...

Можно в отчете в RunTime подключить и источник данных. Как оговаривалось выше эти функции выполняют основные классы: TRaveDataView, TRaveDataField и собственно сам источник данных, например TRvDataSetConnection.

  1. Условимся, что некий источник данных в проекте уже существует и содержит некоторые данные. В данном случае рассмотрим пример подключения к текстовому типу поля таблицы. Для начала нам потребуется «связной» с источником данных – TRaveDataView. Для того, чтобы созданный «связной» присутствовал в проекте отчета Rave Report его нужно добавить в список подключенный модулей данных, воспользовавшись методом «Add» свойства «DataObjectList» у класса TRaveProjectManager. У созданного объекта TRaveDataView достаточно заполнить четыре основных свойства: Name – имя компонента, Parent – родительский компонент (TRaveProjectManager), DataCon.Connection – подключаемый источник данных (в данном примере TRvDataSetConnection), ConnectionName – имя подключаемого источника данных.
  2. Далее необходимо найти необходимое поле в источнике данных TDataSet воспользовавшись методом «FindField» и выяснить к какому типу данных это поле принадлежит. После определения типа поля в источнике данных необходимо создать соответствующее (совместимое) поле данных TRaveDataField для отчета Rave Report. Для TRaveDataField также достаточно заполнить четыре основных свойства: Name – имя компонента, FieldName – имя поля, Parent – родительский компонент (TRaveDataView), DataIndex – порядковый номер в списке полей источника данных (таблице).
  3. При необходимости в отчете можно создать TRaveRegion, TRaveDataBand, TRaveDataText (если не созданны). Будем считать, что данные объекты уже созданы в проекте отчета Rave Report. Для TRaveDataBand, TRaveDataText следует указать поставщика источников данных

(TRaveDataView) воспользовавшись свойством «DataView» этих классов. Для TRaveDataText следует дополнительно указать имя поля, из которого будет производиться выборка данных, для чего следует обратиться к свойству «DataField» этого класса.

Пример реализации подключения источника данных приведен ниже. Данный пример присоединяет источник данных к вновь созданному отчету ('MyNewReport' – рассмотренному выше).

...
var
  TmpRaveReport: TRaveReport;
  TmpDataView: TRaveDataView;
  TmpDataField: TRaveDataField;
  TmpRaveRegion: TRaveRegion;
  TmpRaveDataBand: TRaveDataBand;
  TmpRaveDataText: TRaveDataText;
  TmpField: TField;
begin
  TmpDataField := nil;
  if not RvProjectRTR.SelectReport(NewRep, true) then
    EXIT;
  if (ADOTableRTR.FieldCount < 1) or (not ADOTableRTR.Active) then
    EXIT;
  // Поиск строкового поля
  TmpField := RvDataSetConnectionRTR.DataSet.FindField(FieldStr);
  if TmpField = nil then
    EXIT;
  TmpRaveReport := RvProjectRTR.ProjMan.ActiveReport;
  // Создание TRaveDataView обеспечивающий работу с источником данных
  TmpDataView := TRaveDataView.Create(RvProjectRTR.ProjMan);
  RvProjectRTR.ProjMan.DataObjectList.Add(TmpDataView);
  with TmpDataView do
  begin
    Name := NewDataView;
    Parent := RvProjectRTR.ProjMan;
    DataCon.Connection := RvDataSetConnectionRTR;
    ConnectionName := TmpDataView.DataCon.Connection.Name;
  end;

  // В зависимости от типа данных поля создается поле данных для отчета
  // Rave Report соотвествующего типа
  case TmpField.DataType of
    ftString,
      ftWideString: TmpDataField := TRaveStringField.Create(TmpDataView);
    ftInteger: TmpDataField := TRaveIntegerField.Create(TmpDataView);
  end;
  if TmpDataField = nil then
    EXIT;
  // Заполение свойств поля данных
  with TmpDataField do
  begin
    Name := TmpField.Name; // Имя компонента
    FieldName := TmpField.FieldName; // Имя поля
    FullName := TmpField.DisplayName; // Альтернативное имя поля
    // Порядковый номер в списке полей источника данных (таблице)
    DataIndex := TmpField.Index;
    Parent := TmpDataView; // Родительский компонент
  end;
  // Создание TRaveRegion на котром будут расположены компоненты для работы с источником данных
  TmpRaveRegion := TRaveRegion.Create(TmpRaveReport.FirstPage);
  with TmpRaveRegion do
  begin
    Name := 'NewRaveRegion';
    Parent := TmpRaveReport.FirstPage;
    Left := SetPointInPercent(TmpRaveReport.FirstPage.PageWidth, 5);
    Top := SetPointInPercent(TmpRaveReport.FirstPage.PageHeight, 15);
    Width := SetPointInPercent(TmpRaveReport.FirstPage.PageWidth, 70);
    Height := SetPointInPercent(TmpRaveReport.FirstPage.PageHeight, 60);
  end;
  // Создание TRaveDataBand для размещения елементов оформления отчета на нем
  TmpRaveDataBand := TRaveDataBand.Create(TmpRaveRegion);
  with TmpRaveDataBand do
  begin
    Name := 'NewDataBand';
    Parent := TmpRaveRegion;
    Left := SetPointInPercent(TmpRaveRegion.Width, 5);
    Top := SetPointInPercent(TmpRaveRegion.Height, 5);
    Width := SetPointInPercent(TmpRaveRegion.Width, 90);
    Height := SetPointInPercent(TmpRaveRegion.Height, 5);
    DataView := TmpDataView;
  end;
  // TRaveDataText обеспечит отображение информации из источника данных
  TmpRaveDataText := TRaveDataText.Create(TmpRaveDataBand);
  with TmpRaveDataText do
  begin
    Name := 'NewDataText';
    Parent := TmpRaveDataBand;
    Left := SetPointInPercent(TmpRaveDataBand.Width, 1);
    Top := SetPointInPercent(TmpRaveDataBand.Height, 1);
    Width := SetPointInPercent(TmpRaveDataBand.Width, 50);
    DataView := TmpDataView;
    DataField := TmpDataField.FieldName;
  end;
  RvProjectRTR.ExecuteReport(NewRep);
  ...

Создав новый отчет или изменив существующий можно сохранить внесенные изменения. Чтобы ваши труды не пропали даром, измененный проект отчета можно сохранить, воспользовавшись методом «Save». Если возникнет необходимость сохранить проект отчета Rave Report под другим именем, то следует изменить имя файла проекта в свойстве «ProjectFile» или воспользоваться методом «SetProjectFile» перед сохранением проекта. Все данные свойства и методы доступны у менеджера отчета TRvProject или TRaveProjectManager. Пример сохранения проекта отчета под другим именем:

...
// Сохраниение новго отчета под именем 'TestSave.rav'
RvProjectRTR.SetProjectFile('TestSave.rav');
RvProjectRTR.Save;
...

Скачать пример можно здесь

P.S. Ну, вот вроде и все о чем хотелось поведать. Надеюсь после выхода данной статьи, у пользователей генератора отчета Rave Report работа хоть сколько-то облегчится. Если вы заметили какие-либо ошибки, недочеты в данной статье, а также если у вас есть какие либо предложения или дополнения к данной статье, то все ваши отзывы будут приятны здесь: Sun-bittern@mail.ru

Турушев Виталий (Sun bittern ©)
Нижний Тагил 2004г.

Литература:
1. Стив Тейксейра и Ксавье Пачеко «DELPHI 5 Руководство разработчика. Том 2. Разработка компонентов и работа с базами данных».

Проект Delphi World © Выпуск 2002 - 2024
Автор проекта: USU Software
Вы можете выкупить этот проект.