Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
 
компьютерный мастер стоимость адекватна

Работа с отчетами Rave Report в режиме RunTime

приложение к статье
run_time_rep.zip

Введение

  С выходом 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('Имя класса:                 	  %s', [Class_PTI.Name]));
    Add(Format('Тип класса:                 	  %s', [GetEnumName(TypeInfo(TTypeKind), 
						  Integer(Class_PTI.Kind))]));
    Add(Format('Размер объекта:             	  %d', [ClassX.InstanceSize]));
    Add(Format('Описан в модуле:            	  %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. Разработка компонентов и работа с базами данных".
   Внимание! Запрещается перепечатка данной статьи или ее части без согласования с автором. Если вы хотите разместить эту статью на своем сайте или издать в печатном виде, свяжитесь с автором.
Автор статьи:  Турушев Виталий
  

Другие статьи Наверх


  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо