Einzelnen Beitrag anzeigen

Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.186 Beiträge
 
Delphi 11 Alexandria
 
#13

AW: Seltsames Verhalten der Destruktoren

  Alt 12. Dez 2017, 20:12
Hi zusammen

Sorry, wenn ich erst jetzt antworte - Hausmannspflichten haben gerufen, bzw. sich schon fast die Seele aus dem Leib geschrien...

Die beiden Frame-Instanzen werden nicht zur Laufzeit erstellt, sondern per Klick im OI auf Frames und nach Klick auf die Mainform/eine Panelgroupseite zw ein Panel ähnlich wie eine Kompoonwnte an der gewünschten Stelle angelegt. Beide Instanzen erben also nur den Constructor-Code des Basisframes:
Delphi-Quellcode:
constructor TPDFiumFrame.Create(AOwner: TComponent);
begin
{$IFDEF TRACK_EVENTS}
  AllocConsole;
{$ENDIF}
  inherited;
  ControlStyle := ControlStyle + [csOpaque];
  FZoom := 100;
  FPageIndex := -1;
  PDFPageClass := TPDFPageClass.Create;
  FSelBmp := TBitmap.Create;
  FSelBmp.Canvas.Brush.Color := RGB(50, 142, 254);
  FSelBmp.SetSize(100, 50);

  FPages := TList.Create;
  FReportList := TStringlist.Create;
  FReportList.Sorted := False;
  FGetPageAt := 0;
  try
    FPDF_InitLibrary;
  except
    FStatus := TLabel.Create(Self);
    FStatus.Align := alClient;
    FStatus.Parent := Self;
    FStatus.Alignment := taCenter;
    FStatus.Layout := tlCenter;
    FStatus.Caption := sUnableToLoadPDFium;
  end;
end;
Wie ich auch in andern Threads schon gesagt habe, geht es darum, 2 Frames, oder eben besser, 2 Frameinstanzen, die auf dem selben Basisframe beruhen, zu synchronisieren. Im Anhang mal ein Screenshot meiner Testanwendung: Links der kleine, schmale Frame zeigt mehrere Seiten des Dokumentes. Ein Klick auf solch eine (z.B. die 3.) soll nun diese Seite in der rechten, grösseren Frameinstanz anzeigen.
Nach längerer Zeit hab ichs dann geschafft, dass in PDFiumFrame1 für jede angeklickte Seite auch dren korrekter Index, bzw. die Seitennummer, angezeigt wurde:
Delphi-Quellcode:
procedure TSynpdfMain.PDFiumFrame1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
  var SP, FP,MousePos :TPoint; Index : Integer; Page : TPDFiumFrame.TPDFPageClass;
begin
  Memo1.Lines.Clear;
  Memo1.Lines.Add('------ PDFiumFrame1MouseUp ------');
//(1)
  MousePos.X:= X;
  MousePos.Y:= y;
  Page := PDFiumFrame1.PageAt[MousePos];
//(2)
  PDFiumFrame2.PageIndex := Page.Index;
// PDFiumFrame2.PDFSelPage.Index := Page.Index;
  PDFiumFrame2.SetChoosePage(Page.Index);
end;
Obiger Code entstand in den letzten Tagen; an den mit //(1) bezeichneten Stellen befindet sich Code, der mir zum einen die Seitengrössen und Indexe in PDFiumFrame1 in ein Memo ausgibt und zum andern bei (2) den Index und den Klassennamen der angeclickten Seite.
Die Methode PDFiumFrame1.PageAt[MousePos]; ist eine vom Basisframe geerbte Funktion. Und Page schliesslich ist ein Property vom Typ TPDFPage des Basisframes. Letztere Klasse ist allerdings im Basisframe privat deklariert und somit eigentlich unsichtbar. Mit DeddyHs Hilfe hab ichs dann doch geschafft. Ein Ausschnitt aus der Deklaration des Basisframes :
Delphi-Quellcode:
    procedure SetPageIndex(Value : Integer);
  public
    { Déclarations publiques }
    type
      TPDFPageClass = TPDFPage;
  public
    PDFPageClass : TPDFPageClass;
     
    constructor Create(AOwner: TComponent); override;
Aus dem Constructor des Frames:

Delphi-Quellcode:
  FPageIndex := -1;
  PDFPageClass := TPDFPageClass.Create;
... und einer aus der Mainform:
Delphi-Quellcode:
procedure TSynpdfMain.PDFiumFrame1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
  var MousePos :TPoint; Index : Integer; Page : TPDFiumFrame.TPDFPageClass;
begin
...
...
  MousePos.X:= X;
  MousePos.Y:= y;
  Page := PDFiumFrame1.PageAt[MousePos];
Und schliesslich befindet sich die in TSynpdfMain.PDFiumFrame1MouseUp aufgerufene Methode PDFiumFrame2.SetChoosePage(Page.Index) ebenfalls im Basisframe und wird von diesem an seine Instanzen vererbt.:

Delphi-Quellcode:
procedure TPDFiumFrame.SetChoosePage(Value: Integer);
begin
  if Self.PageIndex <> Value then
    Self.PageIndex := Value;
  Self.LoadVisiblePages;
  Application.ProcessMessages;
end;
Die aufgerufene Methode LoadVisiblePages:

Delphi-Quellcode:
procedure TPDFiumFrame.LoadVisiblePages;
var
  Index, Z, Marge: Integer; Page : TPDFPage; LSelectedPage : TRectD;
  Top, Scale : Double; LClient, LRect : TRect;
begin
  FPageIndex := -1;
  FCurPage := nil;
  for Index := 0 to FPages.Count - 1 do
  begin
    Page := FPages[Index];
    if Page.Selection = nil then
      Dec(Page.Visible);
    else
      Page.Visible := 0;
  end;
  LClient := ClientRect;
  Top := 0;
  Z := 0;
  Marge := PAGE_MARGIN; //const PAGE_MARGIN = 5; // pixel
  Scale := FZoom / 100 * Screen.PixelsPerInch / 72;
  for Index := 0 to FPageCount - 1 do
  begin
  // compute page position
    LRect.Top := Round(Top * Scale) + Marge - VertScrollBar.Position;
    LRect.Left := PAGE_MARGIN + Round((FTotalSize.cx - FPageSize[Index].cx) / 2 * Scale) - HorzScrollBar.Position;
    LRect.Width := Round(FPageSize[Index].cx * Scale);
    LRect.Height := Round(FPageSize[Index].cy * Scale);
    if LRect.Width < LClient.Width - 2 * PAGE_MARGIN then
      LRect.Offset((LClient.Width - LRect.Width) div 2 - LRect.Left, 0);
  // visibility test
    if LRect.IntersectsWith(LClient) then
    begin
      if FPageIndex < 0 then
        FPageIndex := Index;
      Page := GetPage(Index);
      Page.Rect := LRect;
      Page.Visible := 1;
    end;
  // don't go below LClient area
    if LRect.Top > LClient.Bottom then
      Break;
  // next page top position
    Top := Top + FPageSize[Index].cy;
    Inc(Marge, PAGE_MARGIN);
  end;
  // release any page that was not visibile for the last 5 paint events
  for Index := FPages.Count - 1 downto 0 do
  begin
    Page := FPages[Index];
    if Page.Visible < -5 then
    begin
      Page.Free;
      FPages.Delete(Index);
    end;
  end;
end;
Eigentlich hatte ich gehofft, dass ich mit der Übergabe des in PDFiumFrame1 ermittelten Seitenindexes an PDFiumFrame2 hier so die entsprechende Seite anzeigen lassen könnte. Das funktioniert so aber nicht.
Vielmehr denke ich, dass hier die eigentliche Quelle des deerzeitigen Problems steckt. Zur Erinnerung:
Delphi-Quellcode:
procedure TSynpdfMain.PDFiumFrame1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
  var MousePos :TPoint; Index : Integer; Page : TPDFiumFrame.TPDFPageClass;
begin
...
...
  MousePos.X:= X;
  MousePos.Y:= y;
  Page := PDFiumFrame1.PageAt[MousePos];
Hier wird von PDFiumFrame1.PageAt eine Seite zurückgegeben. Deren Index wird in TPDFiumFrame.SetChoosePage dem Property PageIndex des Basisframes zugewiesen. Dmit verfügt der Basisframe über ein Seitenhandle mehr, als in der Liste Pages gespeichert sind. Diese Liste wird bei Programmende freigegeben - nicht aber die Seite, die sich (noch) nicht in der Liste befindet, weshalb das Dokument nicht freigegeben werden kann.
So zumindest meine bisherige Erklärung für das zur Zeit auftretende Problem. Der Basisframe verfügt über ein Boolean-Feld FReload. Dessen Verwendung:
Delphi-Quellcode:
procedure TPDFiumFrame.PaintWindow(DC: HDC);
var
  Index : Integer;
  Page : TPDFPage;
  Client: TRect;
  WHITE : HBrush;
  SelDC : HDC;
  Blend : TBlendFunction;
begin
  FInvalide := False;
...
// check visibility
  if FReload or (FPages.Count = 0) then //<==
  begin
    LoadVisiblePages;
    PDFiumFramePaintWindowToMemo1;
    FReload := False;
  end;
// page background
Diese Art der Programmierung mit dauerndem Laden/Entladen der Seitenhandles ist für mich sehr ungewöhnlich, weshalb ich alles andere als sicher bin, die Arbeitsweise des Programms wirklich verstanden zu haben.

Gruss
Delbor
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch

Geändert von Delbor (12. Dez 2017 um 21:20 Uhr)
  Mit Zitat antworten Zitat