Динамическое отображение таблиц

отредактировано 07:11 Раздел: FastReport 4.0
Доброго времени суток). Что-то никак не могу разобраться. У меня есть переменный набор групп. Их точное количество вычисляется. Нужно отобразить где-то так:

Имя группы

Таблица значений

Имя следующей группы

Таблица значений

и т.д.

Может кто натолкнет на мыслю. Пишу в delphi

Комментарии

  • отредактировано 07:11
    Таблица значений - это один и тот же Датасет (набор данных, SQL-запрос, ...) или разные? От этого зависит модель.
  • отредактировано 07:11
    Nightmareterrible написал: »
    Таблица значений - это один и тот же Датасет (набор данных, SQL-запрос, ...) или разные? От этого зависит модель.

    Ну тут смотря какой SQL напишу. Правда потом у меня он в массив преобразовывается и пользуюсь UserDataSet. Могу сделать выборку, чтобы все в одном запросе было, могу чтобы в разных и передавать несколько раз. Но что-то мне кажется, что в одном массиве как-то проще будет. Только не могу понять как его разделить. А то у меня три раза одни и те же данные выводит. :) .
  • отредактировано 07:11
    Случай 1. У нас только один MasterData (то есть один SQL-запрос, один датасет, как хотите)
    Исходя из задачи, которую вы преследуете предложу 2 варианта:
    1) Если таких групп должно быть точное число (2, 5, 6), то можно создать некий параметр вида:
    Select .... (Param1 + Param2 * 10 +  Param3 * 100 + Param4 * 1000 ...) as PARAMETER
    
    В дизайне отчёта добавляете один frxGroupHeader по полю PARAMETER и анализируете его либо посредством вложенных IIF, либо в onBeforePrint по средством CASE. И выводите тот текст, что нужно

    2) Если мы заранее не знаем, сколько таких групп, то создаём группы динамически
    var
      i: Integer;
      frxHeader:TfrxGroupHeader;
      frxFooter:TfrxGroupFooter;
      k : extended;
    begin
      k := TfrxMasterData(frxReport1.FindComponent('MasterData1')).Top;
      for i := 0 to 3 do
      begin
        frxHeader := TfrxGroupHeader.Create(frxReport1.FindObject('Page1'));
        frxHeader.CreateUniqueName;
        frxHeader.Top :=  k;
        // создание компонентов (Memo, Child и прочих)
        k := k - (6+frxHeader.Height)*fr01cm;
      end;
    
      k := TfrxMasterData(frxReport1.FindComponent('MasterData1')).Top+
      TfrxMasterData(frxReport1.FindComponent('MasterData1')).Height;
      for i := 0 to 3 do
      begin
        frxfooter := TfrxGroupFooter.Create(frxReport1.FindObject('Page1'));
        frxFooter.CreateUniqueName;
        frxfooter.Top := k;
        // создание компонентов (Memo, Child и прочих)
        k:=k+6 + (frxFooter.Height)*fr01cm;
      end;
    end;
    
    Только надо определиться, каким образом будет передаваться параметр группировки, но это уже зависит от конкретной задачи, которую решаете (от БД)

    Случай 2. У нас несколько SQL-запросов в разных TDataSet
    Здесь вообще элементарно:

    Header1
    MasterData1
    Footer1
    Header2
    MasterData2
    Footer2
    и так далее
    Можно создавать динамически, можно в дизайнере. Это зависит, опять же таки, от того, знаем ли мы точное количество групп.
  • отредактировано July 2013
    Эх как-то тяжко дается мне Fast Report ( . А почему у меня в этом коде не выводиться Группа 2 ?
  • отредактировано July 2013
    Точнее скажем вот такой код
    procedure TForm1.Button1Click(Sender: TObject);
    var
      Band: TfrxBand;
      Memo: TfrxMemoView;
      Page: TfrxReportPage;
      i : integer;
      N : integer;
      currenttop : integer;
    begin
    
      frxReport1.Clear;
      Page := TfrxReportPage.Create(frxReport1);
      Page.CreateUniqueName;
      Page.SetDefaults;
      Band := TfrxReportTitle.Create(Page);
      Band.CreateUniqueName;
      Band.Top := 0;
      Band.Height := 20;
      currenttop := 0;
      N:=10;
      for i := 0 to N do
      begin
        currenttop := currenttop +20;
        Memo := TfrxMemoView.Create(Band);
        Memo.CreateUniqueName;
        Memo.Text := 'Группа ' + inttostr(i);
        Memo.Height := 20;
        Memo.Align := baWidth;
    
        Band := TfrxPageHeader.Create(Page);
        Band.CreateUniqueName;
        Band.Top := currenttop;
        Band.Height := 20;
      end;
    
      frxReport1.ShowReport;
    end;
    

    выводит только Группу 0 и Группу 1 (((
  • отредактировано 07:11
    Не очень понимаю, зачем frxBand.
    И рекомендую использовать fr01cm или fr1cm, цифра "20" не факт, что даст корректный результат.
    Пришлите тестовый проект с кусочком базы данных, попробую разобраться.
  • отредактировано 07:11
    Добавьте в конце строчку frxReport1.SaveToFile('d:\234.fr3') и посмотрите на макет в дизайнере. Галимотья какая-то получается.
    Смотрите вложения.
    234.fr3 и 1.jpg - ваш отчёт.
    123.fr3 и 2.jpg - мой отчёт.
  • отредактировано 07:11
    В принципе, ситуация с группировкой может быть не стандартной.
    Если хотите, пришлите пример готового отчёта (рисунок или WORD) и кусок имеющейся базы или её макет, смогу если не помочь, то хотя бы подсказать.
  • отредактировано July 2013
    Поидее должно получиться, вроде этого ( в картинке) . Тут я попаинтил. только сколько групп я заранее не знаю. А данные в таблице берутся из запроса типа. Там они должны быть разные...

    Группа 1 - имя пользователя - тел - ....
    Группа 1 - имя пользователя - тел - ....
    Группа 1 - имя пользователя - тел - ....
    Группа 2 - имя пользователя - тел - ....
    Группа 2 - имя пользователя - тел - ....
    Группа 3 - имя пользователя - тел - ....
  • отредактировано July 2013
    А по какому условию они должны разбиваться по группам?

    Можно, например, так:
    Select {... поля ...}, Group_Number --хранится в базе данных
    Select {... поля ...}, (Select <выражение>) as Group_Number --вычисляется

    Добавляем на форму frxDBDataset1
    В отчёте создаём один TfrxGroupHeader (+ Footer). Можно в дизайнере. Бросаем на него Memo1
    В onBeforePrint этого GroupHeader1 пишем:
    case <frxDBDataSet1."Group_Number"> of
    begin
      1:
         Memo1.Text := 'Группа 1';
      2:
         Memo1.Text := 'Группа 2';
      3:
         Memo1.Text := 'Группа 3';
    end;
    
    А можно вообще в SQL-запросе написать поле GroupName и вообще никакого Case не надо, тупо в Memo пишем [frxDBDataSet1."GroupName"]

    Это так, в общих чертах. Более детально подскажу, если увижу базу, и как вы из неё берёте данные.
  • отредактировано July 2013
    Ещё могу посоветовать пробежаться по демо-примерам отчётов. Классная штука, скачать можно здесь: http://www.fast-report.com/ru/download/
    Выбираете FastReport VCL -> Demo -> compiled Demo.

    В самом демо есть пример отчёта "Drill-down groups".
    Кнопки Preview и Design покажут вам, как это работает.
  • отредактировано July 2013
    просто я не пойму, у меня получается один дата сет. И в отчете он 3 раза
    т.е.

    MasterData1 = DataSet
    MasterData2 = DataSet
    MasterData3 = DataSet

    но при первом проходе мне надо вывести элементы 1 группы
    при втором элементы 2 группы
    при третьем элементы 3 группы и т.д.

    написал что-то тестовое. Вроди бы подходит. Но не пойму как в дата сете установить первый элемент. Или чем можно заменить Value := '' чтобы оно просто ничего не выводило.
    implementation
    
    {$R *.DFM}
    
    const
      ar: array[0..9] of Integer = (0,1,2,3,4,5,6,7,8,9);
      RangeArray : array[0..2,0..1] of integer =((0,5),(6,7),(8,9));
    var
    
      i : integer;
      j: integer;
    procedure TForm1.ArrayDSFirst(Sender: TObject);
    begin
      j:=0;
      inc(i);
    end;
    
    procedure TForm1.ArrayDSNext(Sender: TObject);
    begin
       inc(j);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      i:= -1;
      j:= 0;
      ArrayDS.RangeEnd := reCount;
      ArrayDS.RangeEndCount := 10;
      frxReport1.ShowReport;
    end;
    
    procedure TForm1.frxReport1GetValue(VarName: String; var Value: Variant);
    begin
      Value:='';
      if CompareText(VarName, 'element') = 0 then
      begin
        if (j>=RangeArray[i,0]) and (j<=RangeArray[i,1]) then
          Value := ar[ArrayDS.RecNo];
      end;
    end;
    
  • отредактировано 07:11
    Не могу понять: почему вы вводите значения вручную вместо того, чтобы использовать базу данных (DataSet)?
    Чтобы не выводить что-то, нужно это скрыть
    TfrxMasterData(...).Visible := (<условие> = true);
    TfrxMemoView(...).Visible := (<условие> = true);
  • отредактировано July 2013
    ну visible это ж всего объекта. А у меня одной строки только. и там ж от First до Eof оно три раза проходит. И если я Visible поставлю, то оно как-то его ж менять придется.

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

    А через датасет как? Я же не могу привязать мастер даты к разным датасетам. Или мне динамически придется создавать TFrxUserDataSet, что у меня не получилось. Или я чего-то непонимаю :)
  • отредактировано 07:11
    У вас небольшая ошибка в коде - не frxReport1GetValue, а DataSetGetValue
    В общем, анализируйте. После 17:00 уже не отвечу. Исходники прикрепил
    То или не то?
    unit Unit7;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, frxClass;
    
    type
      TForm7 = class(TForm)
        frxReport1: TfrxReport;
        frxUserDataSet1: TfrxUserDataSet;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure frxUserDataSet1Next(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure frxUserDataSet1Prior(Sender: TObject);
        procedure frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean);
        procedure frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
      TRecord1 = record
        text:string;
        grouptype:integer;
      end;
    
    var
      Form7: TForm7;
      index:integer;
      array1:array of trecord1;
      groups:array[0..2] of string=('Группа 1', 'Группа 2', 'Группа 3');
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm7.Button1Click(Sender: TObject);
    begin
       frxReport1.ShowReport;
    end;
    
    procedure TForm7.FormCreate(Sender: TObject);
    var
      i,j:integer;
      s:string;
      count:integer;
    begin
      index := 0;
      randomize;
      setlength(array1,100);
      for i := 0 to high(array1) do
      begin
        count := random(90)+10;
        s :='';
        for j := 0 to count do
          s := s+ chr(random(60)+32);
        array1[i].text := s;
        array1[i].grouptype := random(high(groups)); // Если нужно по порядку, то тогда сами решайте как заносить
      end;
    end;
    
    procedure TForm7.frxUserDataSet1CheckEOF(Sender: TObject; var Eof: Boolean);
    begin
      Eof := Index >= High(array1);
    end;
    
    procedure TForm7.frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
    begin
      if VarName = 'Text' then
        Value := array1[Index].text
      else if VarName = 'GroupType' then
        Value := array1[Index].grouptype
      else if VarName = 'GroupName' then
        Value := groups[array1[Index].grouptype]
    end;
    
    procedure TForm7.frxUserDataSet1Next(Sender: TObject);
    begin
      if (Index <= High(array1)) then
        inc(INdex)
    end;
    
    procedure TForm7.frxUserDataSet1Prior(Sender: TObject);
    begin
      if INdex >0 then
        dec(index);
    end;
    
  • отредактировано July 2013
    О ) то что надо. Благодарю) :) Осталось только впихнуть вместо text : string динамический массив 2х2 с постоянным количеством колонок и переменным столбцов.

    TRecord1 = record
    text: string;
    grouptype:integer;
    end;

    поменять на

    TRecord1 = record
    text:array[0..5] of TStringList;
    grouptype:integer;
    end;

    Завтра помучаю). Но пол дела сделано ).
  • отредактировано July 2013
    Получилось. Поставил свойство UserDataSet RangeEnd в reCount и поменял функцию на вот такую

    procedure TForm7.frxUserDataSet1GetValue(const VarName: string; var Value: Variant);
    begin
    if VarName = 'Text' then
    begin
    Value := array1[Index].text[row];
    inc(row);
    inc(countRow);
    if countRow=recordCount then
    begin
    countRow := 0;
    frxUserDataSet1Next(self);
    end;
    end
    else if VarName = 'GroupType' then
    Value := array1[Index].grouptype
    else if VarName = 'GroupName' then
    Value := groups[array1[Index].grouptype]
    end;

    Теперь у меня при достижении нужного номера вызывается событие frxUserDataSet1Next(self);

    а в Prior задается количество строк в следующем списке

    procedure TForm7.frxUserDataSet1Prior(Sender: TObject);
    begin
    recordCount := recordCount+1; // тут любое количество
    if INdex >0 then
    dec(index);
    { row:=0; }
    end;

    вобщем демо код готов. Можно его внедрять в проект). Nightmareterrible большое спасибо). :)
  • отредактировано 07:11
    Рад, что смог помочь.

Оставить комментарий

Многофункциональный текстовый редактор. Чтобы отредактировать стиль параграфа, нажмите TAB, чтобы перейти к меню абзаца. Там вы можете выбрать стиль. По умолчанию не выбран ни один стиль. Когда вы выберете текст, появится встроенное меню форматирования. Нажмите TAB, чтобы войти в него. Некоторые элементы, такие как многофункциональные вставки ссылок, картинок, индикаторов загрузки и сообщений об ошибок могут быть вставлены в редактор. Вы можете перемещаться по ним, используя стрелки внутри редактора и удалять с помощью клавиш delete или backspace.