Interactive Report - pagebreak not updated

edited 1:28PM in FastReport 4.0
After changing an interactive report, only the first page is repainted. Pagebreaks are not updated. Longer text is lost.

I'm working on an interactive report, which contains a TfrxRichView. The RichView contains a longer text, 3 pages, with some database-fields. The report is loaded, database-fields are filled, Preview is shown.
On doubleklick the user can edit the RichView-Text. The RichView-Text is loaded into an editor, changed and stuffed backed into the RichView. Here is the code:
procedure TForm.FRDB1DblClickObject(Sender: TfrxView;
  Button: TMouseButton; Shift: TShiftState; var Modified: Boolean);
var
  text: string;
  stream: TStringStream;
begin
  if Sender.ClassType = TfrxRichView then
  begin
    Editor := TEditorForm.Create(self);
    text := TfrxRichView(Sender).GetComponentText;
    Editor.execute(text);
    stream := TStringStream.Create(text);
    TfrxRichView(Sender).RichEdit.Lines.LoadFromStream(stream);
    Modified := true;
    Editor.Free;
    stream.Free;
  end;
end;

It???s works just fine till here. The Preview shows the changed text. But when the changes are longer, so the pagebreak should be modified, it isn???t. The preview seems to repaint only the first page. Text, which doesn???t fit in, is simply cut off and lost. It???s lost not only in the preview, but also if printed.

If I add a
Report.PrepareReport
The changes are lost and the report shows the original text. How can I get a full repaint after changing?

Comments

  • edited 1:28PM
    doncht wrote: »
    Looks like a tough one. Any updates on this yet? I would suggest to create a support ticket about this too. They may need to duplicate this issue first.

    I'm working on a solution. I will post it here, when I have solved the problem.

    My experiences with support tickets at fast-report are not really good. I have currently a ticket on a problem with RTF-Export running for weeks without any answer.
  • edited May 2013
    OK, here is my solution for the problem:
    You have to change the views text and then rerun the report.

    Solution 1:
    This solution gives the user the power to change the template of the report, after looking at the preview. It will be like editing a serial letter in MS Word.
    procedure TForm.EditReport(Report: TfrxReport; View: TfrxView;
      Button: TMouseButton; Shift: TShiftState; var Modified: Boolean);
    var
      frxComp: TfrxComponent;
      p: integer;
      stream: TStringStream;
      text: string;
    begin
    //1.
      if (View.ClassType = TfrxRichView) or (View.ClassType = TfrxMemoView) then
      begin
        text := '';
        Editor := TEditorForm.Create(self);
        Editor.WindowState := wsMaximized;
        Editor.RichEdit.PlainText := not (View.ClassType = TfrxRichView);
    
    //2.
        //Find the Object for edit and get the objects text in the variable text
        frxComp := Report.FindObject(View.Name);
        if frxComp.ClassType = TfrxRichView then
        begin
          stream := TStringStream.Create('');
          TfrxRichView(frxComp).RichEdit.Lines.SaveToStream(stream);
          text := stream.DataString;
          stream.Free;
        end
        else if View.ClassType = TfrxMemoView then
          text := TfrxMemoView(frxComp).Text;
    
    //3.
    If Editor.execute(text) then
        begin
          // change the original content of the View to the edited, rerun the report
          if frxComp.ClassType = TfrxRichView then
          begin
            stream := TStringStream.Create(text);
            TfrxRichView(frxComp).RichEdit.Lines.LoadFromStream(stream);
            stream.Free;
          end
          else if frxComp.ClassType = TfrxMemoView then
          begin
            TfrxMemoView(frxComp).Text := text;
          end;
          Report.ShowReport;
        end;
    
        Editor.Free;
      end;
    end;
    

    //1.
    In the first part I mostly initialize my editor. As you can see, I extended my approach, so you can edit RTF- and Memo-Views.

    //2.
    The OnDoubleClickObject- or the OnClickObject-Event of a TfrxReport gives you the clicked object as Sender: TfrxView. But this Sender is not the view in the template, but it is the view in the preview. If the view in the preview is broken over several pages, fast-report copies the view to each page and fill each of it with the content belonging to this page. So you can???t simply get the content of the sender, because you will get only the part of the content, which is written on the page, which has been clicked. We have to find the belonging view in the template and get his content.

    If you are working with RTF you can???t simply use the text-property of the field. It will give you the plaintext without the control characters. Using streams is, as far as I know, the only way to get the not interpreted text.

    //3.
    After editing the text, I put it back into the template and rerun the report.

    Solution 2:
    In the above version we edited the template of the report. Databasefields will be shown.
    You can also edit the prepared text, but it???s a little bit more complicated.
    procedure TForm.FRDB1DblClickObject(Sender: TfrxView;
      Button: TMouseButton; Shift: TShiftState; var Modified: Boolean);
    var
      text: string;
      stream: TStringStream;
      frxComp: TfrxComponent;
      RV: TfrxRichView;
      MV: TfrxMemoView;
      p: integer;
    begin
      if (Sender.ClassType = TfrxRichView)
      or (Sender.ClassType = TfrxMemoView) then
      begin
        Editor := TEditorForm.Create(self);
    
    //1.
        //read text from Preview
        text := '';
        stream := TStringStream.Create('');
        for p := 0 to FRDB1.PreviewPages.Count - 1 do
        begin
          frxComp := FindPreviewObject(FRDB1, p, Sender.Name);
          if frxComp <> NIL then
            if frxComp.ClassType = TfrxRichView then
            begin
              RV := TfrxRichView(frxComp);
              stream.Free;
              stream := TStringStream.Create('');
              RV.RichEdit.Lines.SaveToStream(stream);
    //2.
              text := ConcatRTF(text, stream.DataString);
            end
            else if frxComp.ClassType = TfrxMemoView then
            begin
              MV := TfrxMemoView(frxComp);
              text := text + MV.Text;
              Editor.RichEdit.PlainText := true;
            end;
        end;
    
    
        Editor.WindowState := wsMaximized;
        If Editor.execute(text) then
        begin
    //3.
          //reopen report and replace text
          stream.Free;
          stream := TStringStream.Create(text);
          FRDB1.LoadFromFile(FRDB1.FileName);
          if Sender.ClassType = TfrxRichView then
          begin
            RV := TfrxRichView(FRDB1.FindObject(Sender.Name));
            RV.RichEdit.Lines.LoadFromStream(stream);
          end
          else if Sender.ClassType = TfrxMemoView then
          begin
            MV := TfrxMemoView(FRDB1.FindObject(Sender.Name));
            MV.Text := text;
          end;
          FRDB1.ShowReport;
        end;
    
        Editor.Free;
        stream.Free;
      end;
    

    //1.
    This time we have to read the content from the preview. We can access the preview by frxReport.PreviewPages??¦ but there is a problem. The preview is separated in its pages. The pages are a container for the objects on the page. If a view is broken over several pages, there will be a copy of the view in each page. In my case the text runs over 3 pages. So there is a TfrxRichview with the name ???Rich1??? in page 1, one in page 2 and one in page 3. Each of the views carries part of the text. But we want all of it. So we have to iterate over the pages, find the view on each page and get the content. I wrote an extra procedure for this: FindPreviewObject
    function FindPreviewObject(Report: TfrxReport; PreviewPage: integer;
      ObjectName: string): TfrxComponent;
    
      function GetChildObjects(frxComponent: TfrxComponent): TArrayOfTfrxComponent;
      var
        i, j, lnResult, lnChilds: integer;
        Childs: TArrayOfTfrxComponent;
      begin
        SetLength(result, 0);
        if frxComponent <> NIL then
        begin
          SetLength(result, frxComponent.AllObjects.Count);
          for i := 0 to frxComponent.AllObjects.Count - 1 do
            result[i] := frxComponent.AllObjects[i];
          lnResult := length(result);
          for i := 0 to length(result) - 1 do
          begin
            Childs := GetChildObjects(result[i]);
            lnChilds := length(Childs);
            Setlength(result, lnResult + lnChilds);
            for j := 0 to lnChilds - 1 do
              result[lnResult + j] := Childs[j];
          end;
        end;
      end;
    
    var
      i: integer;
      Childs: TArrayOfTfrxComponent;
      str: string;
    begin
      result := NIL;
      if Report <> NIL then
      begin
        Childs := GetChildObjects(Report.PreviewPages.Page[PreviewPage]);
    
        str := '';
        for I := 0 to length(childs) - 1 do
          str := str + Childs[i].Name + #13#10;
        showMessage(str);
    
        for I := 0 to length(childs) - 1 do
          if Childs[i].Name = ObjectName then
          begin
            result := Childs[i];
            break;
          end;
      end;
    end;
    

    Many of the objects in the preview are container. So you have to search for childs in every object.

    //2.
    Now we have the view, but to edit the text, we have to join its parts. You can???t simply sum it together. RTF is a tag operated format. Every RTF-document starts with {/rtf.. and ends with }. If you just sum the parts together, you get something like this:

    {/rtf
    content content content
    }
    {/rtf
    content content content
    }
    {/rtf
    content content content
    }

    A RTF-editor will show only the first part, because for it there is a ???Document End???-Tag at the first }. So we have to delete these tags. This is the purpose concatRTF
    function ConcatRTF(String1, String2: string): string;
    var
      RTFTag1, RTFTag2, RTFBody1, RTFBody2: string;
    begin
      SliceRTF(String1, RTFTag1, RTFBody1);
      SliceRTF(String2, RTFTag2, RTFBody2);
      if RTFTag1 = '' then
        RTFTag1 := RTFTag2;
      result := RTFTag1 + RTFBody1 + RTFBody2 + '}';
    end;
    
    function SliceRTF(aRTFString: string; out RTFTag: string;
      out RTFBody: string): Boolean;
    var
      x, lnString, OpenBrackets: integer;
      found: Boolean;
    begin
      result := false;
      aRTFString := trim(aRTFString);
      if pos('{\rtf', aRTFString) = 1 then
      begin
        result := true;
        //find the end of the content
        found := false;
        OpenBrackets := 0;
        x := 2;
        lnString := Length(aRTFString);
        While (not found) and (x <= lnString) do
        begin
          inc(x);
          if aRTFString[x] = '{' then inc(OpenBrackets)
          else if aRTFString[x] = '}' then
          begin
            dec(OpenBrackets);
            if OpenBrackets = 0 then
              found := true;
          end;
        end;
        //copy body
        RTFTag := copy(aRTFString, 1, x);
        RTFBody := copy(aRTFString, x+1, Length(aRTFString) - x - 1);
      end;
    end;
    

    //3.
    Finally I reload the template, change the content of the template view with the edited content and rerun the report.

    But there is a problem with this solution. It works fine, when your report has just one Dataset. It fails, when there is more than one. When the preview contains more than one repetitions of the template, you will find many views with the same name on the preview pages, but not all of them belong to another. It???s complicated to find the break between the repetitions. And even worse, I haven???t found a way to know, on which repetition the user clicked. The content will be equal at diverse points. The name of the view is always the same. There is no clue, on which page the user clicked. If you use the upper solution, you???ll get repeated text.

Leave a Comment

Rich Text Editor. To edit a paragraph's style, hit tab to get to the paragraph menu. From there you will be able to pick one style. Nothing defaults to paragraph. An inline formatting menu will show up when you select text. Hit tab to get into that menu. Some elements, such as rich link embeds, images, loading indicators, and error messages may get inserted into the editor. You may navigate to these using the arrow keys inside of the editor and delete them with the delete or backspace key.