Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Dynamische Pages (TAdvOfficePage bzw. TAdvOfficePager)


LittleBen - Fr 22.04.11 19:41
Titel: Dynamische Pages (TAdvOfficePage bzw. TAdvOfficePager)
Hallo,
habe mal wieder ein kleines Problemchen. Es geht um das erzeugen, aber viel wichtiger, um das löschen von Pages auf einem Pager. Dazu verwende ich Komponenten der TMS-Bibliothek(TAdvOfficePage bzw. TAdvOfficePager).
Eine Page zu erzeugen ist kein Problem, doch eine zu löschen schon...

Der Vorgang:
Ich habe eine Procedure die RefreshTab heißt. Diese löscht zuerst alle Tabs(auser den Ersten), und erzeugt dann wieder so viele Tabs, wie die StringList Items hat. Doch wenn ich mich gerade in einem Tab(nicht im Ersten) aufhalte und dann diese Procedure aufrufe, dann tritt ein Fehler(meist Zugriffsverletzung) auf. Doch das komische ist, dass dieser Fehler nur manchmal auftritt, auch wenn ich immer das selbe mache.
Wenn ich mich im ersten Tab aufhalte funktioniert alles.

Schaut es euch einfach mal an:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
procedure TF_Essen.RefreshTab;
var aPage: TAdvOfficePage;
    aButton: TButton;
    i: integer;
    sl: TStringlist;
begin
 sl:= TStringlist.Create;
            
 //Alle Tabs, auser den Ersten, löschen
 for i:= oReg1.AdvPageCount-1 downto 1 do //oReg1 = TAdvOfficePager 
 begin
  oReg1.AdvPages[i].free; //Hier steckt der Fehler
 end;
                        
 //StringList füllen
 sl.Add('TEST'+inttostr(1));
 sl.Add('TEST'+inttostr(2));
 sl.Add('TEST'+inttostr(3));
 sl.Add('TEST'+inttostr(4));
 sl.Add('TEST'+inttostr(5));
             
 //Nun die Pages erstellen 
 for i:= 0 to sl.count-1 do
 begin
  aPage:= TAdvOfficePage.Create(self);
  aPage.AdvOfficePager:= oReg1;
  aPage.Caption:= sl.Strings[i];
  aPage.Name:= 'Page'+inttostr(i+2);
  aPage.Tag:= i+2;
              
  aButton:= TButton.Create(Self);
  aButton.Parent:= oReg1.AdvPages[oReg1.AdvPageCount-1];
  aButton.Caption:= 'Refresh '+IntToStr(i+1);
  aButton.Name:= 'b_refresh'+IntToStr(i+2); 
  aButton.SetBounds(100,12+i*(aButton.Height+4),aButton.Width,aButton.Height);
  aButton.Tag:=i+2;
  aButton.OnClick:= b_refresh1Click; //RefreshTab aufrufen
 end;
                       
 sl.Free;
end;

Habe mich dann ein bisschen schöau gemacht und den Tipp gelesen, dass man mit die Pages mit Postmessage freigeben soll.
Also habe ich das getan. Hat sich dadurch auch deutlich verbessert!!

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
const
  PM_FREEPAGE = CM_Base+1;
...
         
procedure TF_Essen.RefreshTab;
var ...
begin
...
 for i:= oReg1.AdvPageCount-1 downto 1 do //oReg1 = TAdvOfficePager 
 begin
  PostMessage(Handle,PM_FREEPAGE,i,0);
  Application.ProcessMessages;
 end;
...
end;


Delphi-Quelltext
1:
2:
3:
4:
procedure TF_Essen.PMFREEPAGE(var Message: TMessage);
begin
 oReg1.AdvPages[Message.WParam].Free;
end;


Nun kommen nur abwechselnd irgendwelche Exceptions, etc...
Doch ich mach immer genau das Selbe:
Wechseln zu Page3-->Button drücken-->Wechseln zu Page3-->Button drücken-->Wechseln zu Page3-->Button drücken-->...
Und irgendwann kommt dann mal ein Fehler!
Warum?

Ich hoffe, dass ich mein Problem gut genug geschildert habe, sodass ihr mir auch helfen könnt :)

Freu mich schon auf eure Ideen!

Danke&Grüße
Benny


jaenicke - Fr 22.04.11 20:40

Ich kenne die Komponente eigentlich nicht, habe sie aber einmal kurz installiert. Da fällt mir sofort RemoveAdvPage beim Pager ins Auge. Hast du das einmal damit versucht statt die Seite einfach freizugeben?

Bei mir funktioniert das damit problemlos, ohne irgendwelche Messages oder irgendetwas Besonderes. :nixweiss:


LittleBen - Fr 22.04.11 21:44

Also wenn ich es so mache:

Delphi-Quelltext
1:
2:
 for i:= 2 to oReg1.AdvPageCount do
  oReg1.RemoveAdvPage(TAdvOfficePage(FindComponent('Page'+inttostr(i))));

und dann wieder meine Pages erstelle, kommt sofort der Fehler, dass Page2 schon vorhanden ist.
Ich denke dass RemoveAdvPage die Page nicht wirklich Destroyed.

Grüße
Benny


jaenicke - Fr 22.04.11 21:47

Na gut, das hatte ich jetzt natürlich nicht getestet. Und wie sieht es mit einer Kombination aus beidem aus? Erst entfernen, dann freigeben? :zwinker:


LittleBen - Fr 22.04.11 21:52

Das funktioniert dann, wenn ich mich im ersten Tab aufhalte.
Wenn nicht, dass gibt es wieder, aber nur Manchmal, eine Zugriffsverletzung...


Delphi-Quelltext
1:
2:
3:
4:
5:
 for i:= oReg1.AdvPageCount-1 downto 1 do
 begin
  oReg1.RemoveAdvPage(TAdvOfficePage(FindComponent('Page'+inttostr(i+1))));
  TAdvOfficePage(FindComponent('Page'+inttostr(i+1))).Free;
 end;


Als ich nach dem Problem gegooglet hatte, bin ich ja auf den Tipp mit Postmessage gekommen.
Hier die Erklärung dafür:
Zitat:
PostMessage wird hier verwendet, weil PostMessage ans Ende der Botschafts-
warteschlange gestellt wird und deshalb erst abgearbeitet wird wenn alle
anderen Botschaft, wie z.B. die Click Botschaft an den Button abgearbeitet
wurde


EDIT: Zurzeit kommen auch Fehlermeldungen wie "Priviligierte Anweisung" oder "Abstrakter Fehler"


jaenicke - Fr 22.04.11 22:25

Ja, wenn du auch die Seite freigeben willst, auf der der Button ist, so dass der mit freigegeben wird... :shock:

Dann musst du das eben alles kombinieren, Message schicken, dort Seiten entfernen und freigeben.


LittleBen - Fr 22.04.11 22:27

Wie meinen?

Edit: Also meinst du, dass ich den Button auch(manuell) freigeben muss?


jaenicke - Fr 22.04.11 22:48

Ok, ich habe es ausprobiert. Und dabei dann ClearAdvPages gesehen. So klappt es bei mir absolut problemlos:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
const
  WM_TEST = WM_USER + 3290;

type
  TForm746 = class(TForm)
    TestPager: TAdvOfficePager;
    procedure FormCreate(Sender: TObject);
  private
    { Private-Deklarationen }
    procedure RecreatePagesClick(Sender: TObject);
  protected
    procedure WmTest(var Msg: TMessage); message WM_TEST;
  public
    { Public-Deklarationen }
  end;

var
  Form746: TForm746;

implementation

{$R *.dfm}

procedure TForm746.RecreatePagesClick(Sender: TObject);
begin
  PostMessage(Handle, WM_TEST, 00);
end;

procedure TForm746.FormCreate(Sender: TObject);
begin
  RecreatePagesClick(Sender);
end;

procedure TForm746.WmTest(var Msg: TMessage);
var
  i: Integer;
  NewPage: TAdvOfficePage;
  NewButton: TButton;
begin
  TestPager.ClearAdvPages;

  for i := 0 to Random(10) + 1 do
  begin
    NewPage := TAdvOfficePage.Create(Self);
    NewPage.AdvOfficePager := TestPager;
    NewPage.Caption := 'Page ' + IntToStr(i);
    NewPage.Name := 'Page' + IntToStr(i);

    NewButton := TButton.Create(Self);
    NewButton.Parent := NewPage;
    NewButton.Caption := 'Refresh ' + inttostr(i + 1);
    NewButton.Name := 'b_refresh' + inttostr(i + 2);
    NewButton.SetBounds(10012 + i * (NewButton.Height + 4), NewButton.Width, NewButton.Height);
    NewButton.Tag := i + 2;
    NewButton.OnClick := RecreatePagesClick;
  end;
end;


LittleBen - Fr 22.04.11 22:56

Vielen,vielen Dank!
Vorallem dass du dir die Mühe gemacht hast!

Doch ich musste erschreckend feststellen, dass ich diese Funktion nicht habe :(
Könntest du sie vielleicht posten?

Vielen Danke!


jaenicke - Fr 22.04.11 23:07

Ich habe nur die Trial, also keine Quelltexte. ;-)

Aber dann eben ohne diese Funktion, so funktioniert es ebenfalls problemlos:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, AdvOfficePager, StdCtrls, Generics.Collections;

const
  WM_TEST = WM_USER + 3290;

type
  TForm746 = class(TForm)
    TestPager: TAdvOfficePager;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    FPageList: TObjectList<TAdvOfficePage>;
    procedure RecreatePagesClick(Sender: TObject);
  protected
    procedure WmTest(var Msg: TMessage); message WM_TEST;
  public
    { Public-Deklarationen }
  end;

var
  Form746: TForm746;

implementation

{$R *.dfm}

procedure TForm746.FormDestroy(Sender: TObject);
begin
  FPageList.Free;
end;

procedure TForm746.RecreatePagesClick(Sender: TObject);
begin
  PostMessage(Handle, WM_TEST, 00);
end;

procedure TForm746.FormCreate(Sender: TObject);
begin
  FPageList := TObjectList<TAdvOfficePage>.Create(True);
  RecreatePagesClick(Sender);
end;

procedure TForm746.WmTest(var Msg: TMessage);
var
  i: Integer;
  NewPage, CurrentPage: TAdvOfficePage;
  NewButton: TButton;
begin
  for CurrentPage in FPageList do
    TestPager.RemoveAdvPage(CurrentPage);
  FPageList.Clear;

  for i := 0 to Random(10do
  begin
    NewPage := TAdvOfficePage.Create(nil);
    NewPage.AdvOfficePager := TestPager;
    NewPage.Caption := 'Page ' + IntToStr(i);
    NewPage.Name := 'Page' + IntToStr(i);
    FPageList.Add(NewPage);

    NewButton := TButton.Create(Self);
    NewButton.Parent := NewPage;
    NewButton.Caption := 'Refresh ' + inttostr(i + 1);
    NewButton.Name := 'b_refresh' + inttostr(i + 2);
    NewButton.SetBounds(10012 + i * (NewButton.Height + 4), NewButton.Width, NewButton.Height);
    NewButton.Tag := i + 2;
    NewButton.OnClick := RecreatePagesClick;
  end;
end;
// EDIT:
Ich sehe gerade, dass du nur Delphi 5 und 7 hast? Dann musst du eine normale TObjectList nehmen und statt for..in eine normale for-Schleife. ;-)


LittleBen - Fr 22.04.11 23:18

Genau, habe nur Delphi 5 und 7. Wie meinst du das mit der for-schleife? Bekomme ich irgendwie nicht hin :(

Trotzdem nochmal viiiiielen Dank :)


jaenicke - Fr 22.04.11 23:33

Naja, so halt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, AdvOfficePager, StdCtrls, Contnrs;

const
  WM_TEST = WM_USER + 3290;

type
  TForm746 = class(TForm)
    TestPager: TAdvOfficePager;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
    FPageList: TObjectList;
    procedure RecreatePagesClick(Sender: TObject);
  protected
    procedure WmTest(var Msg: TMessage); message WM_TEST;
  public
    { Public-Deklarationen }
  end;

var
  Form746: TForm746;

implementation

{$R *.dfm}

procedure TForm746.FormDestroy(Sender: TObject);
begin
  FPageList.Free;
end;

procedure TForm746.RecreatePagesClick(Sender: TObject);
begin
  PostMessage(Handle, WM_TEST, 00);
end;

procedure TForm746.FormCreate(Sender: TObject);
begin
  FPageList := TObjectList.Create(True);
  RecreatePagesClick(Sender);
end;

procedure TForm746.WmTest(var Msg: TMessage);
var
  i: Integer;
  NewPage: TAdvOfficePage;
  NewButton: TButton;
begin
  for i := 0 to FPageList.Count - 1 do
    TestPager.RemoveAdvPage(FPageList[i] as TAdvOfficePage);
  FPageList.Clear;

  for i := 0 to Random(10do
  begin
    NewPage := TAdvOfficePage.Create(nil);
    NewPage.AdvOfficePager := TestPager;
    NewPage.Caption := 'Page ' + IntToStr(i);
    NewPage.Name := 'Page' + IntToStr(i);
    FPageList.Add(NewPage);

    NewButton := TButton.Create(Self);
    NewButton.Parent := NewPage;
    NewButton.Caption := 'Refresh ' + inttostr(i + 1);
    NewButton.Name := 'b_refresh' + inttostr(i + 2);
    NewButton.SetBounds(10012 + i * (NewButton.Height + 4), NewButton.Width, NewButton.Height);
    NewButton.Tag := i + 2;
    NewButton.OnClick := RecreatePagesClick;
  end;
end;


LittleBen - Fr 22.04.11 23:39

ES FUNKTIONIERT!!

Vielen Dank für alles!!!!
Bin nun so erlöst :)

Nochmals Danke und eine gute Nacht!

Grüße,
Benny