Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Prüfen ob Button geklickt


Dude566 - Sa 01.05.10 15:51
Titel: Prüfen ob Button geklickt
Hallo,

also ich bin momentan dabei ein kleines Zeichenprogramm zu entwickeln.
Ich habe ähnlich wie Paint eine Toolbar in der ich Buttons für die jeweiligen Zeichenoperationen habe.
(siehe Anhang)

Nun habe ich für jede Zeichenoperation eine eigene Klasse geschrieben, wenn der Button geklickt wird und im folgenden dann der erste Klick auf der Paintbox getätigt wird, soll überprüft werden welche Zeichenoperation gewählt wurde.
Bisher habe ich es so gelöst:
Wenn der jeweilige Button gewählt wird, wird ein String mit dem Namen des Typen in eine globale Variable "typ" geschrieben.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure TForm1.btnLineClick(Sender: TObject);
begin
  typ := 'Line';
end;

procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if typ = 'Line' then
  begin
      MyLine := TLine.Create;
      MyLine.SetColor(ColorDialog1.Color);
      MyLine.SetPointP1(x,y);
  end;
end;


Ich finde das sehr unpraktisch, habt ihr einen Vorschlag für eine bessere Möglichkeit zu überprüfen welche Zeichenoperation gewählt wurde?

Gruß Dude566


Gausi - Sa 01.05.10 16:07

Eine Möglichkeit wäre, bei Klick auf btnLine das OnMouseDown von Paintbox1 zu ändern. Ob das sinnvoller ist, weiß ich aber auch nicht. ;-)


Dude566 - Sa 01.05.10 16:10

Wie meinst du das Verändern? Nenne doch bitte mal ein Beispiel. ;)


elundril - Sa 01.05.10 16:12

Naja, dem MouseDown einfach eine neue Procedure zuweisen.


Delphi-Quelltext
1:
2:
3:
4:
procedure TForm1.btnLineClick(Sender: TObject);
begin
  PaintBox1.OnMouseDown := LineMouseDown;
end;


lg elundril


Gausi - Sa 01.05.10 16:12

Anhand des Bildes habe ich das Programm mal erweitert, damit klarer wird, was ich meine:


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:
procedure TForm1.btnLineClick(Sender: TObject);
begin
  PaintBox1.OnMouseDown := PaintBox1MouseDownLine;
end;

procedure TForm1.btnSquareClick(Sender: TObject);
begin
  PaintBox1.OnMouseDown := PaintBox1MouseDownSquare;
end;

procedure TForm1.PaintBox1MouseDownLine(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
      MyLine := TLine.Create;
      MyLine.SetColor(ColorDialog1.Color);
      MyLine.SetPointP1(x,y);
end;
procedure TForm1.PaintBox1MouseDownSquare(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
      MySquare := TSquare.Create;
      MySquare.SetColor(ColorDialog1.Color);
      MySquare.SetPointP1(x,y);
end;


Dude566 - Sa 01.05.10 16:16

Habt vielen Dank, werde ich gleich mal ausprobieren. :)

Edit: Ich bekomme eine ungenügende External Forward Deklaration angezeigt.

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:
procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseDownLine(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseUpLine(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

procedure TForm1.btnLineClick(Sender: TObject);
begin
  PaintBox1.OnMouseDown := PaintBox1MouseDownLine;
end;

procedure TForm1.PaintBox1MouseDownLine(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  MyLine := TLine.Create;
  MyLine.SetColor(ColorDialog1.Color);
  MyLine.SetPointP1(x,y);
end;

procedure TForm1.PaintBox1MouseUpLine(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  MyLine.SetPointP2(x,y);
  try
    MyLine.Draw(PaintBox1.Canvas);
  finally
    MyLine.Free;
  end;
end;


Liegt ja wohl daran, dass ich keine Implementation für PaintBox1MouseDownLine und PaintBox1MouseUpLine habe, was soll ich tun?
Denn wenn ich eine Leere Implementation der beiden Methoden mache funktioniert es auch nicht, also was sollte darin stehen?


elundril - Sa 01.05.10 17:45

wo hast du die proceduren denn definiert?

lg elundril


Dude566 - So 02.05.10 19:59

Im Implementation Teil. Wo denn auch sonst, deklariert sind sie unter private der Form-Klasse.


MaPsTaR - So 02.05.10 20:22

Bei mir kommt dieser Fehler, wenn ich eine Prozedur zwar im Interface-Teil, aber nicht im Implementation-Teil stehen hab.
-> Fehlerzeile im Compilerfenster anklicken und "F1".
Ich würde vorschlagen, du schaust dir mal die Compiler-Fehlermeldung an, um herauszufinden, welche Prozedure gemeint ist.
Nach dem Code, den du geschickt hast, könnten es

Delphi-Quelltext
1:
2:
3:
4:
procedure PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
procedure PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
sein.


Dude566 - Mo 03.05.10 15:06

Ja es betrifft diese 2 Methoden, und selbst wenn ich beide in den Implementation Teil schreibe tritt der Fehler auf.
Normal sollte diese Meldung ja auftreten wenn ich sie nicht implementiert hätte, was ich aber getan habe, wenn auch ohne Inhalt.


Gausi - Mo 03.05.10 15:16

Und bei der Implementation hast du bei den "leeren" Methoden auch ein TForm1 davor stehen?

Und leere Formular-Methoden werden auch schonmal gerne von der IDE beim compilieren gelöscht. Ein kleiner Kommentar oder ein einzelnes Exit; in der Methode verhindern das. ;-)


Dude566 - Mo 03.05.10 15:23

Also in den letzten Tagen bin ich doch sehr durcheinander, habe tatsächlich den Objektnamen (TForm1) vergessen. :oops:

Problem gelöst!


Martok - Mo 03.05.10 16:54

Problem gelöst, aber nicht unbedingt nach Lehrbuch...

Für die Werkzeugauswahl hat man doch normalerweise eine Menge SpeedButtons, die man zu einer Gruppe zusammenfasst. Dann kann immer nur einer Down sein...

Im Zeichen-Event muss man dann nur noch prüfen, bei welchem denn nun Down gesetzt ist.

Also im Prinzip das Selbe, wie du auch schon gemacht hattest, nur mit "Bordmitteln". Außerdem hat der User so direktes Feedback, was er denn eigentlich für ein Werkzeug nutzt gerade.


Dude566 - Di 04.05.10 17:58

Wie kann ich denn Speedbuttons zu einer Gruppe auf meiner Toolbar einrichten? Ich habe gerade mal zum Test einfach 2 auf die Toolbar gezogen, aber wenn ich einen anklicke wird er nicht "eingedrückt".
Wie stelle ich das an? Das wäre natürlich eine bessere Lösung denn so sieht der Benutzer auch gleich welche Zeichenoperation aktiviert ist.


SvenAbeln - Di 04.05.10 18:41

Schau dir mal die Property GroupIndex an.


Dude566 - Di 04.05.10 18:50

Okay das klappt, allerdings können auch mehrere ausgewählt werden (gedrückt).


SvenAbeln - Di 04.05.10 18:58

Wenn Buttons den selben GroupIndex haben, kann immer nur einer davon ausgewählt sein.


jaenicke - Mi 05.05.10 12:36

Wie wäre es denn mit OOP zu arbeiten für den Zweck?

Einfach eine Elternklasse und für jede Zeichenoperation eine abgeleitete Klasse. Dann muss beim Umschalten der Zeichenoperation nur (z.B. über eine Factory-Klasse) die passende Zeichenklasse erzeugt werden.


Dude566 - Mi 05.05.10 15:32

Was du jetzt im genauen mit "über eine Factory-Klasse" meinst ist mir nicht klar, ich habe doch für jede Zeichenoperation eine Klasse.
Alle diese Klassen sind von einer Klasse TGrafix abgeleitet, in der die Punkte etc enthalten sind.

Beispiel:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
type TJNGrafix = class(TObject)
  private
    FColor : TColor;
    FPen : byte;
    p1, p2 : TPoint;
  public
    procedure SetColor (AFarbe : TColor);
    procedure SetPointP1 (x, y : integer);
    procedure SetPointP2 (x, y : integer);
    procedure SetPen (AWidth : byte);
end;

type TLine = class(TJNGrafix)
  private
  public
    procedure Draw (ACanvas : TCanvas);
end;


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:
procedure TLine.Draw(ACanvas: TCanvas);
begin
  ACanvas.Pen.Color := FColor;
  ACanvas.Pen.Width := FPen;
  ACanvas.MoveTo(p1.X, p1.Y);
  ACanvas.LineTo(p2.X, p2.Y);
end;

// --- Line Drawing Procedures -------------------------------------------------
procedure TForm1.PaintBox1MouseDownLine(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  MyLine := TLine.Create;
  MyLine.SetColor(ColorDialog1.Color);
  MyLine.SetPen(12);
  MyLine.SetPointP1(X,Y);
end;

procedure TForm1.PaintBox1MouseUpLine(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  MyLine.SetPointP2(X,Y);
  try
    MyLine.Draw(PaintBox1.Canvas);
  finally
    MyLine.Free;
  end;
end;


Bin noch nicht so ganz fit in Sachen OOP.


jaenicke - Mi 05.05.10 20:06

Dann ist es doch sehr sehr einfach: ;-)

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:
TForm1 = class(TForm)
private
  FPaintOperation: TJNGrafix;
end;

// und dann bei Umschaltung der Operation:
FPaintOperation := TLinePainter.Create;
// oder:
FPaintOperation := TRectPainter.Create;

// und dann einfach (ohne Unterscheidung der Zeichenoperationen):
procedure TForm1.PaintBox1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  FPaintOperation.SetColor(ColorDialog1.Color);
  FPaintOperation.SetPen(12);
  FPaintOperation.SetPointP1(X,Y);
end;

procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  FPaintOperation.SetPointP2(X,Y);
  FPaintOperation.Draw(PaintBox1.Canvas);
end;
So musst du das Objekt nicht ständig neu erzeugen (sondern nur bei Auswahl einer anderen Form).

Und so nebenbei: Benenne deine Komponenten ordentlich... ;-)


Dude566 - Do 06.05.10 18:03

Also einfach je nach Wahl der Form meiner Instanz der Hauptklasse eine der Unterklassen zuweisen.

Was meinst du mit ordentlich benennen? Ich dachte ich hätte sie schon ordentlich benannt.


jaenicke - Fr 07.05.10 04:44

user profile iconDude566 hat folgendes geschrieben Zum zitierten Posting springen:
Also einfach je nach Wahl der Form meiner Instanz der Hauptklasse eine der Unterklassen zuweisen.
Richtig, das spart dir viel Aufwand und redundanten Code.

user profile iconDude566 hat folgendes geschrieben Zum zitierten Posting springen:
Was meinst du mit ordentlich benennen? Ich dachte ich hätte sie schon ordentlich benannt.
Ja, ich sehe schon, dass du die Buttons benannt hast. Mir waren jetzt Form1 und PaintBox1 aufgefallen. ;-)


Dude566 - Fr 07.05.10 18:04

Ja da mache ich mir nicht die Arbeit die umzubenennen wenn die nur einmal vorkommen und es zu keiner Verwechslung kommen kann.


jaenicke - Fr 07.05.10 18:10

Kennst du die CnWizards [http://cnpack.org/showdetail.php?id=679&lang=en]? Die zeigen einfach ein Umbenennungsfeld an, wenn du eine Komponente aufs Formular legst. Dann hast du da keine zusätzliche Arbeit, musst nur kurz einen Namen eintragen. ;-)


Dude566 - Fr 07.05.10 18:48

Nein, das kannte ich bisher nicht.

Das nenne ich mal ein nützliches Tool, danke user profile iconjaenicke! :zustimm: