FR3 и использование нескольких экземпляров форм

отредактировано 19:21 Раздел: FastReport 3.0
У меня при при создании формы все TDataset автоматечески регистрируются
procedure TForm1.FormCreate(Sender: TObject);
var
  i: integer;
  frxDBDataset: TfrxDBDataset;
begin
  for i:=0 to ComponentCount-1 do
  begin
    if Components[i] is TDataset then
    begin
      frxDBDataset:= TfrxDBDataset.Create(self);
      frxDBDataset.DataSet  := Components[i] as TDataSet;
      frxDBDataset.UserName := Components[i].Name;
      frxReport.DataSets.Add(frxDBDataset);
      frxReport.EnabledDataSets.Add(frxDBDataset);
    end;
  end;
end;

Форма состоит из TfrxReport, TDataset, TDataSource, TDBGrid и кнопки "Печать"
procedure TForm1.Button1Click(Sender: TObject);
begin
  frxReport.LoadFromFile('Test.fr3');
  frxReport.ShowReport;
end;

В самом отчете я в Меню\Отчет\Данные указываю использовать Dataset
Нажимаю кнопку, отчет показывается нормально.
Далее я создаю второй экземпляр формы, не делая Free для первого экземпляра.
Нажимаю кнопку "печать" на второй форме и в отчете вижу данные формы 1
Далее закрываю форму 1
Опять нажимаю кнопку "печать" на второй форме и в отчете вижу уже данные формы 2.

Т.е. у меня Меню\Отчет\Данные есть несколько экземпляров с одним именем.

Раньше когда пользовался FR2, у меня на каждой форме был экземпляр TfrReport, и проблем с обращением к TDataset находящимися на текущей форме не было.
Вопрос: как отобразить содержимое TDataset текущей формы в FR3?
Если нужно могу дать тестовый проект.

Комментарии

  • gpigpi
    отредактировано 19:21
    Попробуйте управлять видимостью датасетов в Меню\Отчет\Данные через TfrxDBDataset.Enabled
  • отредактировано March 2006
    gpi написал:
    Попробуйте управлять видимостью датасетов в Меню\Отчет\Данные через TfrxDBDataset.Enabled

    Модифицировал код следующим образом
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
    begin
      for i:=0 to frxReport.DataSets.Count-1 do
      begin
        frxReport.DataSets[i].DataSet.Enabled :=
          TfrxDBDataset(frxReport.DataSets[i].DataSet).DataSet.Owner = Self;
      end;
    
      frxReport.LoadFromFile('Test.fr3');
      frxReport.ShowReport;
    end;
    
    Теперь в Меню\Отчет\Данные есть только по одмому экземпляру, но печатается так же как и раньше.
  • gpigpi
    отредактировано 19:21
    написал:
    Если нужно могу дать тестовый проект.
    Адрес в ПМ
  • отредактировано 19:21
    Отправил
  • gpigpi
    отредактировано 19:21
    Если нужно одновременно показывать только один отчёт при нескольких открытых формах, то можно использовать DM.frxReport. С Form1 можно убрать frxReport и frxDBDataset1
    Поменять код:
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      DM.frxDBDataset1.Dataset:=IBQuery;
      DM.frxDBDataset1.UserName:='IBQuery';
      DM.frxReport.LoadFromFile('Test.fr3');
      if FMain.CheckBox1.Checked then
        DM.frxReport.DesignReport
      else
        DM.frxReport.ShowReport;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Caption := Name;
    end;
    
    Если отчёты нужно показывать одновременно, то я не уверен, что это можно сделать вообще - отключить датасет для одного отчёта, одновременно показывая его в другом.
  • отредактировано 19:21
    У меня схожая проблема: реализован COM-объект в dll, который умеет печататься, отчет загрузается из шаблона (файла), а не генерится в коде (последнее бы сняло проблему, но сложный отчет трудно создавать динамически), соответственно при одновременном создании двух и более объектов и при попытке сравнить содержимое печатной формы каждого объекта получаем полную лажу, поскольку некий глобальный датасетлист содержит множество одноимённых (юзернейм) датасетов
    Господа нужно решение проблемы - можно конечно каждый COMобъект создавать в своём потоке, но должно же быть что-то проще?
  • отредактировано 19:21
    для amonra - можно регистрировать датасеты непосредственно перед показом отчета и удалять их сразу после его печати, а не при создании формы
  • отредактировано 19:21
    Модернизировал код следующим образом
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
      frxDBDataset: TfrxDBDataset;
    begin
      for i:=0 to ComponentCount-1 do
      begin
        if Components[i] is TDataset then
        begin
          frxDBDataset:= TfrxDBDataset.Create(self);
          frxDBDataset.DataSet  := Components[i] as TDataSet;
          frxDBDataset.UserName := Components[i].Name;
        end;
      end;
    
      DM.frxReport.LoadFromFile('Test.fr3');
    
      if FMain.CheckBox1.Checked then
        DM.frxReport.DesignReport
      else
        DM.frxReport.ShowReport;
    
      for i:=ComponentCount-1 downto 0 do
      begin
        if Components[i] is TfrxDBDataset then
          Components[i].Free;
      end;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
       Caption := Name;
    end;
    
    Все работает правильно, но как то это не по человечески...
    gpi, fnvhope спасибо за помощь
  • отредактировано 19:21
    Если я правильно понял, то суть проблемы в том, что FindGlobalComponent (которым пользуется FR) ищет по уникальному имени компонента, которое у второго экземпляра автоматом меняется. В связи с этим перед загрузкой отчета в своем приложении добавил такой код:
    AnsiString CurrentFormName=AnsiString(Screen->ActiveForm->ClassName()).SubString(2, 255);
    TComponent *cmp=FindGlobalComponent(CurrentFormName);
    if(cmp&&cmp!=Screen->ActiveForm) cmp->Name=CurrentFormName+IntToStr(GetTickCount());
    if(Screen->ActiveForm->Name!=CurrentFormName) Screen->ActiveForm->Name=CurrentFormName;
    
    Думаю перевести на паскаль труда не составит...
  • отредактировано 19:21
    На Delphi этот код выглядит так
     CurrentFormName: string;
      cmp: TComponent;
    begin
      CurrentFormName:=Copy(Self.ClassName, 2, 255);
    
      cmp:=FindGlobalComponent(CurrentFormName);
      if(cmp<>nil) and (cmp <> Self)  then
        cmp.Name := CurrentFormName+IntToStr(GetTickCount());
      if(Self.Name <> CurrentFormName) then
        Self.Name := CurrentFormName;
    

    Но это не помагает.

    Мало того даже если перед печатью я делаю
     DM.frxReport.DataSets.Clear;
      DM.frxReport.EnabledDataSets.Clear;
    
    То все равно печатается не то что нужно
  • отредактировано March 2006
    Конечный код получился такой
    procedure TForm1.Button1Click(Sender: TObject);
    var
      i: integer;
      frxDBDataset: TfrxDBDataset;
      DatasetList: TStringList;
    begin
      DatasetList := TStringList.Create;
      frxGetDataSetList(DatasetList);
      for i:=0 to DatasetList.Count-1 do
      if TfrxDBDataset(DatasetList.Objects[i]).Owner <> DM.frxReport then
        TfrxDBDataset(DatasetList.Objects[i]).free;
      DatasetList.free;
    
      for i:=0 to ComponentCount-1 do
      begin
        if Components[i] is TDataset then
        begin
          frxDBDataset:= TfrxDBDataset.Create(self);
          frxDBDataset.DataSet  := Components[i] as TDataSet;
          frxDBDataset.UserName := Components[i].Name;
          DM.frxReport.DataSets.Add(frxDBDataset);
          DM.frxReport.EnabledDataSets.Add(frxDBDataset);
        end;
      end;
    
      DM.frxReport.LoadFromFile('Test.fr3');
    
      DM.frxReport.ShowReport;
    end;
    
    Только есть ли смысл в следующей части кода?
    DM.frxReport.DataSets.Add(frxDBDataset);
    DM.frxReport.EnabledDataSets.Add(frxDBDataset);
    
  • отредактировано 19:21
    Знаешь, код, когда ты перед печатью регистрировал, а после печати - удалял был естественне, чем твой последний. В первом случае компоненты удалял именно тот, кто из зарегистрировал, а в последнем варианте ты перед печатью удаляешь чужие компоненты - ПЛОХО. Управлять надо стараться только своими компонентами.

    GlobalComponentName вам господа не помого по той причине, что все TfrxDataSet при создании регистрируются в неком глобальном списке (см. исходники TfrxDataSet.Create
      DatasetList.Lock;
      DatasetList.Add(Self);
      DatasetList.Unlock;
    
    ), из которого птотм по UserName (обратите внимание, что это свойство заполнится автоматически, если вы его не заполните) через frxFindDataSet вынимается.
    Пишу и вдруг подумалось: попробуй овнером TfrxDataSet сделать report - тогда эта frxFindDataSet будет отрабатывать правильно:
    frxDBDataset:= TfrxDBDataset.Create(Report);
    
  • отредактировано 19:21
    написал:
    Пишу и вдруг подумалось: попробуй овнером TfrxDataSet сделать report - тогда эта frxFindDataSet будет отрабатывать правильно:
    попробовала сама - оказалось, что на эти грабли уже наступала ;) . Не делай этого. Если TfrxDataSet создан не в отчете (в этом случае овнер - Report), а овнер Report - возникают всякие непредвиденные ситуации ...

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

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