Erstellen einer Komponente

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
wennerer
Beiträge: 516
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Erstellen einer Komponente

Beitrag von wennerer »

Hallo,
ich versuche mich an der Erstellung einer eigenen Komponente. Bin eigentlich auch schon ganz zufrieden. Habe aber noch ein verständniss Problem. Ich muss die Startwerte wie Breite und Höhe usw. einmal am Anfang verrechnen bzw. möchte ich auch Einstellungen überprüfen. Wenn ich das in Create mache funktioniert es nur mit den Werten die im Quellcode stehen. Ich verrechne deshalb in Paint. Gibt es eine Procedure die nur beim Erzeugen einmal aufgerufen wird und die Werte aus dem Objektinspektor (oder auch laufzeiterzeugte Werte) verrechnet? Oder macht man das komplett Anders?
Außerdem ist mir noch aufgefallen das ich wenn ich eine Eigenschaft im OI ändere erst einmal in die Form klicken muss damit sich die Komponente ändert (z. Bsp. Farbe). Normal muss ich nur im OI das Feld wechseln.
Viele Grüße
Bernd

Benutzeravatar
kupferstecher
Beiträge: 422
Registriert: Do 17. Nov 2016, 11:52

Re: Erstellen einer Komponente

Beitrag von kupferstecher »

Hallo Bernd,

nach meinem Verständnis nach funktioniert das so: Vorgabewerte programmierst du fest in den Create-Konstruktor ein. Bspw.:
Height:= 123;
Wenn der Wert Height dann im OI geändert wird, wird zur Laufzeit gleich nach Aufrufen des Create-Konstruktors der Setter für Height aufgerufen. Auf diese Weise wird der im Konstruktor gesetzte Wert überschrieben. Deine SetHeight (oder die automatisch gerufene ChangeBounds) muss dann die Berechnungen durchführen, d.h. auf die "neue" Einstellung reagieren. In Anführungsstrichen, da sie ja im OI gesetzt wurde.

wennerer hat geschrieben: Außerdem ist mir noch aufgefallen das ich wenn ich eine Eigenschaft im OI ändere erst einmal in die Form klicken muss damit sich die Komponente ändert (z. Bsp. Farbe).

Fehlt evtl. das "Invalidate" an der richtigen Stelle?

wennerer
Beiträge: 516
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen einer Komponente

Beitrag von wennerer »

Hallo kupferstecher,
vielen Dank für deine Antwort. Zuerst zum Invalidate. Hab in die SetColor ein Invalidate eingefügt und siehe da es funktioniert! Zum Verrechnen muss ich vielleicht noch etwas ausholen. Ich möchte einen kleinen Schieberegler programmieren. Ich habe einen Minimalwert, einen Maximalwert und einen Startwert auf dem der Schieber stehen soll. Ich möchte nun am Anfang überprüfen ob der Startwert zwischen min und max liegt. Woher weiß ich jetzt das setmin und setmax vor setstartwert aufgerufen werden? Oder geht das nach der Reihenfolge in der es im Quelltext steht?
Viele Grüße Bernd

siro
Beiträge: 732
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Erstellen einer Komponente

Beitrag von siro »

Hallo Bernd,

Das sollte in etwa so aussehen, das ist jetzt nciht fertig, sondern soll Dir lediglich als Anleitung dienen:

Code: Alles auswählen

 
Type TSlider = class(TCustomControl)
  private   // hier werden die Eigenschaften der Komponente gespeichert
            // man schreibt hier gerne ein "F" vorne and die Variablen
    FScaleMin : Integer;    // minimal Wert
    FScaleMax : Integer;    // maximal Wert
    FScalePos : Integer;    // aktuelle Schiebeposition
  protected  // diese Funktionen/ Proceuduren sind nur innerhalb der Komponente aufrufbar
    procedure SetScaleMin(newMin:Integer)// setzt den Wert FScaleMin jedoch mit voriger Bereichsprüfung
    procedure SetScaleMax(newMax:Integer);
    procedure SetScalePos(newPos:Integer);
    procedure paint; override;                       // hier wird die Komponente gezeichnet
  public  // öffentliche Proceduren
    constructor create(aOwner:TComponent); override;
//    destructor destroy; override;    // brauchen wir momentan nicht
  published  // diese Eigenschaften sind im Objektinspektor sichtbar / einstellbar
             // wenn man auf den Wert ScaleMin zugreift (Read), wird beim Lesen direkt der Wert FScaleMin zurückgeliefert
             // beim Ändern des Wertes (Write) wird die Procedure SetScaleMin aufgerufen, wo auch die Bereichsprüfung erfolgt
    property ScaleMin  : Integer read FScaleMin write SetScaleMin;
    property ScaleMax  : Integer read FScaleMax write SetScaleMax;
    property ScalePos  : Integer read FScalePos write SetScalePos;
end;
 
 // Dies ist die Procedure zum Setzen von ScaleMin mit der entsprechenden Bereichsprüfung
procedure TSlider.SetScaleMin(newMin:Integer);
begin
  if newMin = ScaleMin then exit;              // Wenn der gleiche Wert gesetzt wird, nix machen
                                               // das beugt auch rekursive Aufrufe vor
 
  if newMin = ScaleMax then newMin:=newMin-1// wenn Max und Min den gleichen Wert annimmt, gibts garantiert später eine Division durch 0
                                               // das verhindern wir damit.
  FScaleMin := newMin;                         // nun übernehmen wir den neuen Wert
 
  // wir müssen nun prüfen ob die Schiebeposition sich noch innerhalb des gültigen Bereichs befindet
 
  if ScalePos < ScaleMin then ScalePos:=ScaleMin;   // ist die Schiebeposition kleiner als Min, dann setzen wir den Schieber auf Min
  if ScalePos > ScaleMax then ScalePos:=ScaleMax;   // ist die Schiebeposition grösser als Max, dann setzen wir den Schieber auf Max
 
  Invalidate;  // Komponente hat sich verändert und soll neu gezeichnet werden
end;
 
procedure TSlider.SetScaleMax(newMax:Integer);
begin
  if newMax = ScaleMax then exit;
  if newMax = ScaleMin then newMax:=newMax+1;
  FScaleMax := newMax;
 
  // Rangecheck für ScalePos
  if ScalePos < FScaleMin then ScalePos:=ScaleMin;
  if ScalePos > FScaleMax then ScalePos:=ScaleMax;
 
  invalidate;
end;
 
procedure TSlider.SetScalePos(newPos:Integer);
begin
  if FScalePos = newPos then exit;
 
  // Rangecheck:
  if newPos < FScaleMin then newPos:=FScaleMin;
  if newPos > FScaleMax then newPos:=FScaleMax;
 
  FScalePos:=NewPos;  // neue Position übernehmen
  Invalidate;
 
end;
 
procedure TSlider.paint;
begin
  // den gesamten Hintergrund ausfüllen
  canvas.Brush.Color:=color;
  canvas.FillRect(ClientRect);
 
  //... hier den rest zeichen
 
end;
 
// Hier machst Du deine grundlegenden Initialisierungen
constructor TSlider.create(aOwner:TComponent);
begin
  inherited;
  SetBounds(0,0,200,16);   // dein Umschliessendes Rechteck wie gross es sein soll (Zeichenbereich)
 
// Deine Grundinitialisierung
  FScaleMin :=0;
  FScaleMax :=100;
  FScalePos :=50;   // Schieber auf Mitte setzen
end;
 
 


Ich hoffe, das hilft Dir etwas weiter

Siro
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

wennerer
Beiträge: 516
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen einer Komponente

Beitrag von wennerer »

Hallo Siro,
vielen Dank für die viele Arbeit. Kann auf Grund deines Beispiels noch etliches in meinem Code verbessern! Ich habe aber noch eins festgestellt.
Wenn ich meine Komponente nicht mit dem OI erzeuge, sondern per Laufzeit, wird z. Bsp. SetScalePos nur durchlaufen wenn ich ScalePos setze. Ändere ich z.Bsp. ScaleMin und /oder ScaleMax so ab das die Voreinstellung von ScalePos ausserhalb liegt und ich merke es nicht (setze also ScalePos auch nicht) wird die Überprüfung von ScalePos nicht durchlaufen. Du machst in deinem Code wahrscheinlich deshalb die Prüfung auch in ScaleMin/Max? Heisst dann für mich ich muss in jeder Set.. Procedure die nötigen Überprüfungen machen wie sich eine Änderung auswirkt.
Nochmal vielen Dank, ich glaube ich hab da einfach zu kompliziert gedacht.
Bernd

Benutzeravatar
kupferstecher
Beiträge: 422
Registriert: Do 17. Nov 2016, 11:52

Re: Erstellen einer Komponente

Beitrag von kupferstecher »

Ich hab deine Frage ja so verstanden, dass du ScalePos im Setter an die Grenzen anpassen möchtest, und deshalb die Aufrufreihenfolge (Min, Max und Pos) stimmen muss, wenn die Werte im OI gesetzt wurden.

Die Antwort darauf weiß ich nicht, ich würde aber vermuten, dass die Reihenfolge durchlaufen wird, wie die Variablen als Published deklariert wurden. Das kannst du ja mal ausprobieren. Der relevante Abschnitt wäre also:

Code: Alles auswählen

  published
    property ScaleMin: Integer read FScaleMin write SetScaleMin;
    property ScaleMax: Integer read FScaleMax write SetScaleMax;
    property ScalePos: Integer read FScalePos write SetScalePos;



wennerer hat geschrieben:Heisst dann für mich ich muss in jeder Set.. Procedure die nötigen Überprüfungen machen wie sich eine Änderung auswirkt.

Einfach die Überprüfung in eine eigene Prozedur auslagern, sie wird ja bei Min, Max und Pos benötigt.

Code: Alles auswählen

Procedure TSlider.EnforcePosMinMax;
begin
  if ScalePos < ScaleMin then ScalePos:=ScaleMin;
  if ScalePos > ScaleMax then ScalePos:=ScaleMax;
end;

wennerer
Beiträge: 516
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen einer Komponente

Beitrag von wennerer »

Hallo Kupferstecher,
auch dir noch mal vielen Dank. Ich werde eure Anregungen jetzt mal durch probieren und schauen mit was ich am besten zurecht komme! Jedenfalls habe ich jetzt etliche Ansatzpunkte.
Viele Grüße Bernd

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

Re: Erstellen einer Komponente

Beitrag von wp_xyz »

kupferstecher hat geschrieben:... ich würde aber vermuten, dass die Reihenfolge durchlaufen wird, wie die Variablen als Published deklariert wurden.

Da würde ich mich nicht drauf verlassen, vielleicht kommt jemand daher wie ich, der die Properties gerne in alphabetische Reihenfolge sortiert...

Also, wenn es auf die Reihenfolge der Abarbeitung ankommt, kannst du die Loaded-Methode überschreiben. Wenn diese drankommt, sind alle Properties geladen, und du hast nun alle Werte zur Verfügung für Folgeaktionen. Wenn es schädlich ist, dass vorher beim Laden der Setter ausgeführt wird, kann man dies mit "if csLoading in ComponentState" abfangen.

Code: Alles auswählen

procedure TSlider.Loaded;
begin
  inherited;
  if FScalePos < FScaleMin then
    FScalePos := FScaleMin
  else if FScalePos > FScaleMax then
    FScalePos := FScaleMax;
end;
 
procedure TSlider.SetScalePos(NewValue: Integer);
begin
  if FScalePs = NewPos then exit;
  if (csLoading in ComponentState) then
    FScalePos := NewPos
  else begin
    ... (dein Code)
  end;
end;
 
// analog für ScaleMin und ScaleMax

wennerer
Beiträge: 516
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen einer Komponente

Beitrag von wennerer »

Hallo wp_xyz,
das mit der Loaded Methode kommt meiner ursprünglichen Idee natürlich am Nähesten. Vielen Dank für deine Antwort. Ich werde mich dann mal weiter dran versuchen.
Viele Grüße Bernd

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

Re: Erstellen einer Komponente

Beitrag von wp_xyz »

Das noch: In der Deklaration von Loaded das "override" nicht vergessen.

Code: Alles auswählen

type
  TSlider= class (TCustomControl);
  ...
  protected
     procedure Loaded; override;
    ...

wennerer
Beiträge: 516
Registriert: Di 19. Mai 2015, 20:05
OS, Lazarus, FPC: Linux Mint 20 Cinnamon,Lazarus 2.2.6 (rev lazarus_2_2_6) FPC 3.2.2 x86_64-linux-
CPU-Target: x86_64-linux-gtk2

Re: Erstellen einer Komponente

Beitrag von wennerer »

Ja danke, werde dran denken.
Viele Grüße Bernd

Benutzeravatar
kupferstecher
Beiträge: 422
Registriert: Do 17. Nov 2016, 11:52

Re: Erstellen einer Komponente

Beitrag von kupferstecher »

@wp: Danke, wieder was gelernt!

Antworten