Die ganze Panel Klasse ansprechen

Für allgemeine Fragen zur Programmierung, welche nicht! direkt mit Lazarus zu tun haben.
Boneless
Beiträge: 52
Registriert: So 1. Dez 2013, 14:15
OS, Lazarus, FPC: Windows 7 (L 1.0.14 - FPC 2.6.0)
CPU-Target: 64Bit
Wohnort: Ostsee

Die ganze Panel Klasse ansprechen

Beitrag von Boneless »

Hallo liebes Deutsche Lazarusforum,

nur ganz kurz zu Anfang: Ich habe das Programm Lazarus im Informatik-Unterricht kennengelernt und sollen nun ein wenig Aufgaben machen und uns etwas damit beschäftigen.
Ich habe zwar schon etwas Programmierkenntnisse und verstehe diese Sprachen Pascal und Delphi auch recht gut.

Nun zu meinem Problem:
Wir sollen eine Anwendung erstellen, die aus 12 Panels aufgebaut ist. Diese werden mit den entsprechenden Button zwecks Visible und Enabled beeinflusst. Da ich aber nicht auf alle 12 Button

Code: Alles auswählen

 
procedure TForm1.Button1Click(Sender: TObject);
begin
Panel1.Enabed := True;
Panel1.Visible := True;
Panel2.Enabed := False;
Panel2.Visible := False;
{Der Rest wieder alles False}
end;
 
procedure TForm1.Button2Click(Sender: TObject);
begin
Panel1.Enabed := False;
Panel1.Visible := False;
Panel2.Enabed := True;
Panel2.Visible := True;
{Der Rest wieder alles False}
end;

usw. immer diesen Code komplett hinschreiben möchte, ist halt meine Frage, ob man das irgendwie so ähnlich wie mit

Code: Alles auswählen

(Sender as TPanel).Enabled

o.ä. lösen könnte?

Vielen Dank schon einmal für eure Aufmerksamkeit.
Lieben Gruß

Boneless

Code: Alles auswählen

program Boneless;
 
 {$mode Einsteiger}{H+}

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: Die ganze Panel Klasse ansprechen

Beitrag von wp_xyz »

Ja, gute Idee. Aber wie sprichst du die anderen Panels an, die disabled werden sollen? Ich würde mir ein Array[0..MAXPANEL] of TPanel deklarieren, dessen Elementen du im FormCreate die Panels zuweist, so dass du in dem Button1Click alle Panels in einer for-Schleife ansprechen kannst.

Boneless
Beiträge: 52
Registriert: So 1. Dez 2013, 14:15
OS, Lazarus, FPC: Windows 7 (L 1.0.14 - FPC 2.6.0)
CPU-Target: 64Bit
Wohnort: Ostsee

Re: Die ganze Panel Klasse ansprechen

Beitrag von Boneless »

Vielen Dank schon mal für deinen Denkanstoß.
Also alle anderen Panels haben immer den Wer False, außer eines (bei jedem Button anders), welches die Werte True bei Enabled und Visible hat.

Ich würde mir ein Array[0..MAXPANEL] of TPanel deklarieren, dessen Elementen du im FormCreate die Panels zuweist, so dass du in dem Button1Click alle Panels in einer for-Schleife ansprechen kannst.

Ja gut. Array und for. Mal sehen

Code: Alles auswählen

 
var ArrPanel: array[1..12] of TPanel;
i: Integer;
procedure TForm1.Button1Click(Sender: TObject);
begin
  for i := 1 to 12 do
  begin
    ArrPanel[i].Enabled := False;
    ArrPanel[i].Visible := False;
  end;
end;


So? :oops:

Code: Alles auswählen

program Boneless;
 
 {$mode Einsteiger}{H+}

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Die ganze Panel Klasse ansprechen

Beitrag von Michl »

Probiers doch so (alle Buttons mit eigener Procedure "ShowPanel" im Ereignis OnKlick belegen - Funktioniert nur, wenn Namen von Button1..12 und Panel1..12 bleiben, ansonsten ... Array):

Code: Alles auswählen

procedure TForm1.ShowPanel(Sender: TObject);
var
  i, ButtonNr, PanelNr: Integer;
  s: String;
begin
  if Sender is TButton then begin
 
    s:=StringReplace(TButton(Sender).Name, 'Button', '', []);
 
    if TryStrToInt(s, ButtonNr) then
      for i:=0 to ComponentCount-1 do
        if Components[i] is TPanel then begin
 
          s:=StringReplace(TPanel(Components[i]).Name, 'Panel', '', []);
 
          if TryStrToInt(s, PanelNr) then
            if PanelNr=ButtonNr then
              TPanel(Components[i]).Visible:=True
            else
              TPanel(Components[i]).Visible:=False;
        end;
  end;
end

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: Die ganze Panel Klasse ansprechen

Beitrag von wp_xyz »

Boneless hat geschrieben:

Code: Alles auswählen

 
var ArrPanel: array[1..12] of TPanel;
i: Integer;
procedure TForm1.Button1Click(Sender: TObject);
begin
  for i := 1 to 12 do
  begin
    ArrPanel[i].Enabled := False;
    ArrPanel[i].Visible := False;
  end;
end;
 


Du musst dann natürlich das Panel, das mit dem Sender identisch ist, noch auf true setzen:

Code: Alles auswählen

 
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer; 
begin
  for i := 1 to 12 do
  begin
    ArrPanel[i].Enabled := False;
    ArrPanel[i].Visible := False;
  end;
  if Sender is TPanel then begin
    TPanel(Sender).Enabled := true;
    TPanel(Sender).Visible := true;
  end;
end;
 
// oder:
procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i:=1 to 12 to begin
    ArrPanel[i].Enabled := (Sender = ArrPanel[i]);
    ArrPanel[i].Visible := (Sender = ArrPanel[i]);
  end;
end;
 


Noch ein paar Tipps:
  • Verwende i nicht als globale Variable, sondern als lokale Variable, damit die Gültigkeit von i nur auf die aktuelle Prozedur beschränkt ist (das Programm kann dann immer wieder i als Zählvariable verwenden, ohne dass du dir den Kopf zerbrechen musst, welches i jetzt gemeint ist.)
  • Deklariere ArrPanel auch nicht als global gültiges Array, sondern als Element von TForm1. Falls dein Programm mehrere Formulare dieser Klasse verwendet, würdest du dasselbe Array für beider Formulare verwenden.
  • Verwende statt der explizt verwendeten 12 eine Konstante, z.B. NUM_PANELS, für die Anzahl der Arrays. Falls du mal 20 Panels hast, musst du dann nur an 1 Stelle ändern.
  • Und ich gehe davon aus, dass in deinem FormCreate-Ereignis die einzelnen Panels den Array-Elementen zugeweisen werden. Also:

Code: Alles auswählen

 
type
  TForm1 = class(TForm)
    procedure Form1Create(Sender: TObject);
    procedure Button1Click(Sender: TObject)// siehe oben
  private
    ArrPanel: Array[1..NUM_PANELS] of TPanel;
  end;
 
procedure TForm1.Form1Create(Sender: TObject),
begin
  ArrPanel[1] := Panel1;
  ArrPanel]2] := Panel2;
  // etc.
end;
 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Die ganze Panel Klasse ansprechen

Beitrag von Michl »

@wp_xyz: Prinzipiell stimme ich mit Dir überein, dass ein Array besser dafür geeignet ist, doch

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i:=1 to 12 to begin
    ArrPanel[i].Enabled := (Sender = ArrPanel[i]);
    ArrPanel[i].Visible := (Sender = ArrPanel[i]);
  end;
end;
wird nicht funktionieren (vorherige Procedure auch nicht), da Sender bei ButtonKlick nicht der Sender von einem TPanel wird.

Dann eher z.B. so:

Code: Alles auswählen

var
  ArrPanel: Array[1..12] of Record
    Panel: TPanel;
    Button: TButton;
  end;
...
procedure TForm1.ShowPanel(Sender: TObject);
var
  i: Integer;
begin
  for i:=1 to 12 do begin
    ArrPanel[i].Panel.Enabled := (Sender = ArrPanel[i].Button);
    ArrPanel[i].Panel.Visible := (Sender = ArrPanel[i].Button);
  end;
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  ArrPanel[1].Panel:=Panel1;
  ArrPanel[1].Button:=Button1;
  ArrPanel[2].Panel:=Panel2;
  ArrPanel[2].Button:=Button2;
  ArrPanel[3].Panel:=Panel3;
  ArrPanel[3].Button:=Button3;
...
end;

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Boneless
Beiträge: 52
Registriert: So 1. Dez 2013, 14:15
OS, Lazarus, FPC: Windows 7 (L 1.0.14 - FPC 2.6.0)
CPU-Target: 64Bit
Wohnort: Ostsee

Re: Die ganze Panel Klasse ansprechen

Beitrag von Boneless »

Ja. Vielen Dank schon mal :)

@ Michl: Dein Code sieht sehr kompliziert aus und ich verstehe ihn auch nich so ganz :oops:

@wp_xyz: Ja, deine Tipps habe ich mir zu Herzen genommen und kenne sie auch schon von C. :lol: Aber wenn ich im FormCreate jedes Panel einen Array Eintrag zugewiesen hab und sie dann mit der for-Schleife abrufe, stürzt das Prog ab :(

Entschuldigt bitte meine unpräzisen Kenntnisse ...

Also erstmal soll ja jedes Panel weg sein.

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  ArrPanel[1] := Panel1;
  ArrPanel[2] := Panel2;
  ArrPanel[3] := Panel3;
  ArrPanel[4] := Panel4;
  ArrPanel[5] := Panel5;
  ArrPanel[6] := Panel6;
  ArrPanel[7] := Panel7;
  ArrPanel[8] := Panel8;
  ArrPanel[9] := Panel9;
  ArrPanel[10] := Panel10;
  ArrPanel[11] := Panel11;
  ArrPanel[12] := Panel12;
 
  for i := 1 to 12 do begin
    ArrPanel[i].Enabled := False;
    ArrPanel[i].Visible := False;
  end;
end;

Code: Alles auswählen

program Boneless;
 
 {$mode Einsteiger}{H+}

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Die ganze Panel Klasse ansprechen

Beitrag von Michl »

Boneless hat geschrieben:@ Michl: Dein Code sieht sehr kompliziert aus und ich verstehe ihn auch nich so ganz :oops:
Ist auch nicht besonders gut zum Verstehen und nicht wirklich sauber, besser Array :wink: - funktioniert aber! 8)

In dem gezeigten Code sollte kein Fehler sein:
Boneless hat geschrieben:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  ArrPanel[1] := Panel1;  //Hier Breakpoint
...
Setz Dir Doch mal einen Breakpoint in deinen Code, starte Dein Prog (mit F9) und gehe schrittweise (mit F7) den Code durch, bis es Probleme gibt und poste dann den Fehler und die Fehlerstelle...

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Boneless
Beiträge: 52
Registriert: So 1. Dez 2013, 14:15
OS, Lazarus, FPC: Windows 7 (L 1.0.14 - FPC 2.6.0)
CPU-Target: 64Bit
Wohnort: Ostsee

Re: Die ganze Panel Klasse ansprechen

Beitrag von Boneless »

Er sagt mir hier ist der Fehler:

Code: Alles auswählen

for i := 1 to 12 do begin
    { --> } ArrPanel[i].Enabled := False; { <-- }
    ArrPanel[i].Visible := False;
  end;                           

Code: Alles auswählen

program Boneless;
 
 {$mode Einsteiger}{H+}

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Die ganze Panel Klasse ansprechen

Beitrag von Michl »

Evtl. ist OnCreate nicht dafür geeignet, weil evtl. Panel.. noch nicht erstellt wurde (evtl. die Sichtbarkeit/Deaktivierung bei OnActivate unterbringen?!)?! Bei mir funktioniert das allerdings, anbei mal mein TestProg...

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Boneless
Beiträge: 52
Registriert: So 1. Dez 2013, 14:15
OS, Lazarus, FPC: Windows 7 (L 1.0.14 - FPC 2.6.0)
CPU-Target: 64Bit
Wohnort: Ostsee

Re: Die ganze Panel Klasse ansprechen

Beitrag von Boneless »

Danke dir!
Ich habe das mal auf mein Prog übertragen und mir kam da ne Frage auf. Hast du evtl. bei deinen Panels irgend ne Eigenschaft verändert?
Weil bei mir kommt jetzt immer die Fehlermeldung:
identifier idents no member "Panel" bei den Zeilen:

Code: Alles auswählen

ArrPanel[i].Panel.Enabled := (Sender = ArrPanel[i].Button);
    ArrPanel[i].Panel.Visible := (Sender = ArrPanel[i].Button);

Code: Alles auswählen

program Boneless;
 
 {$mode Einsteiger}{H+}

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: Die ganze Panel Klasse ansprechen

Beitrag von wp_xyz »

Michl hat geschrieben:wird nicht funktionieren (vorherige Procedure auch nicht), da Sender bei ButtonKlick nicht der Sender von einem TPanel wird.

Folge davon, wenn man nicht sorgfältig genug list (ich), bzw. die Problemstellung nicht genau definiert (Boneless)... Wo ist dieser Button? hat jedes Panel vielleicht einen solchen Button?

Boneless
Beiträge: 52
Registriert: So 1. Dez 2013, 14:15
OS, Lazarus, FPC: Windows 7 (L 1.0.14 - FPC 2.6.0)
CPU-Target: 64Bit
Wohnort: Ostsee

Re: Die ganze Panel Klasse ansprechen

Beitrag von Boneless »

OK an dieser Stelle entschuldige ich mich für die unvollständige Schilderung.

Also:
die vollständige Schilderung

Ich habe vor diese "Schulaufgabe" auf zu Hause zu erweitern. Ich wollte ein Programm schreiben, wo ich alle Setups aufliste, die sich im Laufe der Zeit angehäuft haben und ich sie gerne alle zusammen auf einer CD haben möchte. Zur Veranschaulichung halt dieses Programm bzw. diese Anwendung.
Damit ich dies so anschaulich wie möglich mache, habe ich ein MainMenu mit den MenuItems eingerichtet. Diese sind sozusagen meine Buttons. Jeder dieser MenuItems bekommt ein einziges Panel, wo je ein Setup drauf ist zugewiesen. Aber was auf dem Panel drauf ist, sei dahingestellt. Also wenn ich MenuItem1 anklicke, soll Panel 1 erscheinen. Wenn ich MenuItem9 anklicke, Panel 9 und so weiter.
Somit sind das dann mal 89 Panels :D Da ich dann nicht jedes Mal bei jedem (!) MenuItem jeden einzelnen Panel selbst durchnummerieren möchte, suche ich ja nach einer bequemeren Lösung. Damit a) der Quellcode natürlich "etwas" komprimiert wird und b) ich nicht so viel Schreibarbeit habe.

Code: Alles auswählen

program Boneless;
 
 {$mode Einsteiger}{H+}

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Die ganze Panel Klasse ansprechen

Beitrag von Michl »

Boneless hat geschrieben:identifier idents no member "Panel"

Code: Alles auswählen

var
  ArrPanel: Array[1..3] of Record
    Panel: TPanel;
    Button: TButton;
  end;
!!!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

wp_xyz
Beiträge: 4892
Registriert: Fr 8. Apr 2011, 09:01

Re: Die ganze Panel Klasse ansprechen

Beitrag von wp_xyz »

ok - also Kommando zurück und ganz von vorne: Du hast ein Menu mit MenuItems, bei denen ein Klick ein bestimmtes Panel sichtbar macht? In solchen Fällen verwende ich die "Tag"-Eigenschaft, die jedem Control zugeordnet ist, hier kann man Zusatzinformation speichern, "Tag" wird ansonsten für nichts anderes verwendet. "Tag" ist vom Typ ein PtrInt, das ist ein Integer, der genauso viel Speicher belegt wie ein Pointer - das ist für 32-Bit Programme egal, wird aber bei 64-Bit wichtig, wo Pointer und Integer unterschiedliche Größen haben. Deine Panels, die im Object Inspektor als "Panel1", "Panel2" etc. bezeichnet werden, sind intern nichts anderes als Pointer. Du kannst damit jedem Tag eines MenuItems das zugehörige Panel zuweisen; leider geht das nicht im Object-Inspektor, und du musst das in der FormCreate-Methode erledigen:

Code: Alles auswählen

 
procedure TForm1.FormCreate(Sender: TObject);
begin
  MenuItem1.Tag := PtrInt(Panel1);
  MenuItem2.Tag := PtrInt(Panel2);
  // etc.
end;
 

Dabei bewirkt das PtrInt(...) eine Typ-Umwandlung, d.h. der Compiler wird angewiesen, den Pointer Panel1 als Integer zu interpretieren. Nach dieser längeren Schreibprozedur kennt jeder MenuItem das ihm zugewiesene Panel, und man kann in der OnClick-Routine das Panel aus- und einschalten:

Code: Alles auswählen

 
procedure TForm1.MenuItemClick(Sender: TObject);
var
  panel: TPanel;
  mnuitem: TMenuItem;
begin
  if (Sender is TMenuItem) then begin
    mnuitem := TMenuItem(mnuItem);
    panel := TPanel(mnuItem.Tag);
    panel.Visible := mnuItem.Checked;
    panel.Enabled := mnuItem.Checked;
  end;
end;
 

Hier verwandelt das TMenuItem(..) zunächst den Sender zu einem TMenuItem. Dann wird mit TPanel(...) das dem MenuItem zugewiesene Tag vom Integer in ein Panel verwandelt.

Diese Click-Routine ist natürlich allen MenuItems zuzuweisen. Wenn du willst dass nur eines dieser Menuitems das Häkchen hat, gruppiere sie, indem du allen denselben "GroupIndex" zuweist. Damit sie beim Anklicken auch gleich das Häkchen bekommen, setze das "AutoCheck" von allen auf true.

Klaro?

Antworten