Informatik

Qualifikationsphase 1

Erzeugen von Klassen durch Vererbung

Es soll eine Schaltfläche erzeugt werden, die gegenüber der normalen Schaltfläche zwei Veränderungen aufweist: Eine grüne Einfärbung (Standard ist grau) und einen 3 Pixel breiten Rand (statt 1 Pixel). Die in Delphi oder Lazarus vorhandenen Schaltflächen haben keine Color-Eigenschaft und können auch nicht in ihrer Randbreite verändert werden. Jetzt werden sicherlich einige sagen, dass sie schon farbige Schaltflächen in Programmen gesehen haben. Wie kann man solch eine Komponente verwirklichen? Das Stichwort heißt Vererbung. Eine Lösung soll in vier Schritten gefunden werden.

1. Suche nach der geeigneten Ausgangsklasse

Man versucht z.B. in den reichlich vorhandenen Komponenten eine zu finden, deren Farbe und Randgestaltung leicht verändert werden kann. Die Klasse TPanel wäre dafür ideal. Sie kann ihre Farbe ändern, die Randbreite kann eingestellt werden und sie kann auch angeklickt werden.  Der Nachteil ist jedoch, dass sie den gedrückten Zustand nicht anzeigt. Hier muss nachgeholfen werden.

2. Planung der neuen Komponente

Die Veränderungen der schon vorhandenen Attribute Color und BevelWidth sollen von der neuen Klasse selbst vorgenommen werden. Dazu benötigt man den Konstruktor Create. Alle bisher verwendeten Objekte wurden durch den Konstruktor Create oder CreateForm erzeugt. Jede Objektklasse hat einen Konstruktor. Der benötigte Konstruktor ist eine spezielle Methode, die folgende Aufgaben erfüllt:

1. Er reserviert Speicherplatz, den das Objekt benötigt,
2. er setzt Startwerte, die für die Verwendung des Objekts notwendig sind (Initialisierung),
3. er setzt die für das neue Panel notwendigen Startwerte.

Da die Ursprungsklasse TPanel ebenfalls einen Konstruktor Create besitzt, muss dieser in der abgeleiteten Klasse überschrieben werden, ansonsten würden die alten Startwerte benutzt. Grundsatz: Methoden der Oberklasse sind auch in der Unterklasse verfügbar, solange diese
nicht durch eigene Methoden überschrieben werden.

3. Ableiten der Klasse TGruenPanel aus der Klasse TPanel

Der Konstruktor Create der Oberklasse TPanel wird durch den Konstruktor Create der neuen Unterklasse TGruenPanel überschrieben. Konstruktoren werden mit dem Schlüsselwort constructor eingeleitet. Beim Überschreiben eines Konstruktors müssen die Parameter der alten Methode erhalten bleiben. Der Zusatz override weist Delphi bzw. Lazarus an, den alten Konstruktor zu überschreiben.

type TGruenPanel = class (TPanel)
public
constructor Create (AOwner: TComponent); override;
end;

Beachte: Alle anderen Methoden der Klasse TPanel sind auch für die neue Unterklasse TGruenPanel verfügbar, werden aber nicht noch einmal aufgeführt.

constructor TGruenPanel.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
Color := clGreen;
BevelWidth := 3;
end;

Eine erste Version zur Erzeugung eines farbigen Buttons sieht wie folgt aus:

unit Panel;

nur Lazarus:  {$mode objfpc}{$H+}

interface

Delphi:   uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, StdCtrls, ExtCtrls;

Lazarus: uses Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls;

type TGruenPanel = class (TPanel)
public
constructor Create (AOwner: TComponent); override;
end;

type TForm1 = class(TForm)
BtNeuesPanel: TButton;
procedure BtNeuesPanelClick (Sender: TObject);
private
GruenPanel: TGruenPanel;
end;

var Form1: TForm1;

implementation
Delphi:   {$R *.DFM}

Lazarus: {$R *.lfm}

constructor TGruenPanel.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
Color := clGreen;
BevelWidth := 3;
end;

procedure TForm1.BtNeuesPanelClick (Sender: TObject);
begin
GruenPanel := TGruenPanel.Create (Form1);
GruenPanel.Parent := Form1;
end;

end.

4. Auslagerung in ein eigenständiges Modul

Die Klasse TGruenPanel wird in ein eigenständiges Modul integriert. Dazu wird in Lazarus bzw. Delphi ein neues Modul (Unit) erstellt.

Lazarus:

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils;

implementation

end.

Delphi:

unit Unit1;

interface

implementation

end.

Um die Klasse TPanel benutzen zu können, werden weitere Units benötigt. Lazarus integriert automatisch zwei Units. In beiden Programmen werden die folgenden Klassen benötigt: Classes, Controls, Graphics, Forms und ExtCtrls. Die fertige Unit sieht wie folgt aus:

unit mPanel;

nur Lazarus: {$mode objfpc}{$H+}

interface

uses
Classes, Controls, Graphics, Forms,ExtCtrls;

type TGruenPanel = class(TPanel)
constructor Create (AOwner: TComponent);override;
end;

implementation

constructor TGruenPanel.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
Color := clGreen;
BevelWidth := 3;
end;


end.

Ein buntes Panel

 

Taster und Schalter

Wenn man mit der Maus auf einen Taster klickt, nimmt dieser beim Loslassen der Maus wieder seine ursprüngliche Form an. Beim Schalter bleibt der gedrückte Zustand erhalten, bis man ein zweites Mal auf den Schalter klickt. Es sollen die Klassen TBuntTaster und TBuntSchalter als Unterklasse von TPanel abgeleitet werden.

Die Komponente TPanel besitzt interne Methoden für die Verwaltung der Mausklicks: die Prozeduren MouseDown und MouseUp. Sie sind jedoch inhaltsleer, d.h., sie werden zwar aufgerufen, aber es passiert nichts. Deshalb müssen beide Prozeduren in den abgeleiteten Klassen mit Inhalt gefüllt werden. Neu ist die Schutzklasse Protected. Für den Entwickler ist sie "public", für den Benutzer einer Klasse "private". Beim Entwicklen und Ableiten von Klassen kann man also auf diese Methoden zugreifen, nicht aber während des Programmablaufes.

unit mPanelDemo;

interface

uses ExtCtrls, Classes, Graphics, Forms, Controls, SysUtils;

type TBuntTaster = class (TPanel)
protected
procedure MouseDown (Button: TMouseButton; Shift: TShiftState; X,Y: Integer); override;
procedure MouseUp (Button: TMouseButton; Shift: TShiftState; X,Y: Integer); override;
public
constructor Create (AOwner: TComponent; Farbe: TColor); virtual;
end;

type TBuntSchalter = class (TPanel)
private
Gedrueckt: Boolean;
protected
procedure MouseDown (Button: TMouseButton; Shift: TShiftState; X,Y: Integer); override;
procedure MouseUp (Button: TMouseButton; Shift: TShiftState; X,Y: Integer); override;
public
constructor Create (AOwner: TComponent; Farbe: TColor); virtual;
end;

implementation

{**************************** TBuntTaster ***********************************}

constructor TBuntTaster.Create (AOwner: TComponent; Farbe: TColor);
begin
Inherited Create (AOwner);
Color := Farbe;
BorderStyle := bsNone;
BorderWidth := 1;
BevelOuter := bvNone;
BevelInner := bvRaised;
BevelWidth := 4;
Caption := 'Taster aus';
end;

procedure TBuntTaster.MouseDown (Button: TMouseButton; Shift: TShiftState; X,Y: Integer);
begin
BevelInner := bvLowered;
Caption := 'an';
inherited MouseDown (Button,Shift,X,Y);
end;

procedure TBuntTaster.MouseUp (Button: TMouseButton; Shift: TShiftState; X,Y: Integer);
begin
BevelInner := bvRaised;
Caption := 'aus';
inherited MouseUp (Button,Shift,X,Y);
end;

{********************** TBuntSchalter **************************************}

constructor TBuntSchalter.Create (AOwner: TComponent; Farbe: TColor);
begin
inherited Create (AOwner);
Color := Farbe;
BorderStyle := bsNone;
BorderWidth := 1;
BevelOuter := bvNone;
BevelInner := bvRaised;
BevelWidth := 4;
Caption := 'Schalter aus';
Gedrueckt := False
end;

procedure TBuntSchalter.MouseDown (Button: TMouseButton; Shift: TShiftState; X,Y: Integer);
begin
if Gedrueckt
then begin
BevelInner := bvRaised;
Caption := 'aus';
inherited MouseUp (Button,Shift,X,Y)
end
else begin
BevelInner := bvLowered;
Caption := 'an';
inherited MouseDown (Button,Shift,X,Y)
end;
Gedrueckt := not Gedrueckt
end;

procedure TBuntSchalter.MouseUp (Button: TMouseButton; Shift: TShiftState; X,Y: Integer);
begin
end;

end.

Ereignismethoden von Schalter und Taster

In der Klassendefinition wurden den Tastern und Schaltern interne Eigenschaften zugewiesen. Die Bauvorschrift gibt an, wie ein Schalter bzw. Taster auf der Oberfläche aussieht. Wie erreicht man jedoch, dass eine Ereignismethode ausgeführt wird? Dazu betrachten wir noch einmal die Klasse TPanel. Wenn man ein Panel auf der Form plaziert hat, kann man über den Objektinspektor auswählen, welche Ereignismethode z.B. bei einem Einfachklick ausgeführt werden soll. Hier findet man auch die Ereignisse onMouseUp und onMouseDown. Werden diese Ereignisse im Objektinspektor angewählt, erstellt Delphi/Lazarus automatisch die passende Ereignismethode. Für unsere selbstgeschriebenen Klassen müssen wird dem Programm mitteilen, welche Ereignismethoden bei einem entsprechenden Aufruf ausgeführt werden sollen.

procedure TMain.FormCreate(Sender:TObject);
begin
BuntTaster:=TBuntTaster.Create(Main, clRed);
BuntTaster.PArent:=Main;

Delphi:

BuntTaster.OnMouseDown:= TasterMausUnten;
BuntTaster.OnMouseUp:=TasterMausOben;
BuntTaster.OnClick:=TasterClick;                               // MouseDown + MouseUp

Lazarus:

BuntTaster.OnMouseDown:= @TasterMausUnten;
BuntTaster.OnMouseUp:=@TasterMausOben;
BuntTaster.OnClick:=@TasterClick;
                             // MouseDown + MouseUp

end;

procedure TMain.TasterMausUnten(Sender:TObject; Button:TMouseButton; Shift:TShiftState; X,Y:Integer);
begin
//
end;

procedure TMain.TasterMausOben(Sender:TObject; Button:TMouseButton; Shift:TShiftState; X,Y:Integer);
begin
//
end;

usw.
 

 

V.Berg • Bergisch Gladbach • ImpressumHaftungsausschluss