Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Button in eine Gaugecomponente einfügen?


ALF - Di 19.10.10 15:47
Titel: Button in eine Gaugecomponente einfügen?
Hi,
Wie der Titel schon sagt, überlege ich gerade ob man Buttons in eine Gaugecomponente einfügen kann?
Zusätzlichen Text (nicht den StandartCaption)und dessen Positionierung habe ich ja schon geschaft. Aber 1 oder 2 Buttons, incl ihrer Propertys und Eigenschaften?
Wüsste also nicht mal ansatzweise wie, wenn es überhaupt geht?

Gruss Alf


jaenicke - Di 19.10.10 20:34

Ich weiß nicht, ob TGauge ein Handle hat, so dass du darüber den Parent des Buttons darauf setzen kannst.

Ansonsten: Schreibe doch einfach die komplette Gauge selbst. Viel ist das ja nicht, jedenfalls solange du nur einfache Formen haben möchtest (ich weiß nicht was TGauge so alles macht).

// EDIT:
Naja, also das was TGauge kann lässt sich recht schnell selber schreiben und dann kannst du einfach einen Button reintun.


ALF - Di 19.10.10 21:26

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
......und dann kannst du einfach einen Button reintun.

Naja ich habe ja ne externe Komponente, abgeleitet von TGauge, wie ich da aber einfach nen Button reinbringen soll mit all seinen Möglichkeiten?
Es ist eine Unit die ich im Netz gefunden habe. Meine versuche schlagen da ja irgend wie fehl!

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:
unit GaugeCaption;

interface

uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, StdCtrls, Gauges, StrUtils;

type
  TTemperature= (tNormal, tDegradado, tTemperatura);
  TPosicion= (poIzquierda, poDerecha);
  TGaugeKind = (gkText, gkHorizontalBar, gkVerticalBar, gkPie, gkNeedle);

  TGaugeCaption = class(TGauge)
  private

  FMinValue: Longint;
  FMaxValue: Longint;
  FCurValue: Longint;
  FKind: TGaugeKind;
  FShowText: Boolean;
  FBorderStyle: TBorderStyle;
  FBorderWidth: TBorderWidth;

//---nun dachte ich an sowas-----//
         FButton1: TButton;
         FButton2: TButton;
//--- aber wie kann ich die Buttons in die Graphic einbinden bzw wie komm ich an die eigenschaften?---//

Hab noch nicht mal in irgend einer weise nen Ansatz dafür. Oder das geht vielleicht gar nicht und ich muss mir ne eigene schreiben, wo ich die beiden TGaugeCaption und TButton irgend wie zusammen bekomme!?

Gruss Alf


jaenicke - Di 19.10.10 21:56

Die Eigenschaften sind im Button ja drin. Wenn du eine property unter published erstellst, steht die auch im Objektinspektor. Im Konstruktur müsstest du dann auch die Buttons mit erzeugen und auf die Komponente setzen.

Ich habe aber mittlerweile gesehen, dass ein TGauge gar kein Handle hat. Also kannst du da AFAIK auch keine Komponenten draufsetzen.

Deshalb denke ich einmal selber gleich alles machen geht am einfachsten.


ALF - Di 19.10.10 22:08

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Deshalb denke ich einmal selber gleich alles machen geht am einfachsten.
Jo hab ich befürchtet :( Nur wie bekomme ich beides zusammen?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
unit ButtonGauge;

interface

uses
  SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, StdCtrls, Gauges, StrUtils;


  TButtonGauge = class(??????)
Wovon soll ich das nun ableiten? Da fehlt leider die Erfahrung :oops:

Gruss ALf


FinnO - Di 19.10.10 22:14

Moin,

TGraphicWinControl hat AFAIK ein Canvas, das könnte schonmal hilfreich sein. Ansonsten gibt es für sowas den Komponentenwizard!


ALF - Di 19.10.10 22:50

mh... ne einzelne Komponente zu erstellen trau ich mir ja noch zu, aber ne Komponente die beides beinhaltet?
Dazu mal ein Bild im Anhang, wie ich es meine.
gaugebutton
user profile iconFinnO hat folgendes geschrieben Zum zitierten Posting springen:
Ansonsten gibt es für sowas den Komponentenwizard!
jo, aber deswegen weiss ich nicht wie ich 2 unterschiedlich Komponenten in einer vereinen kann? Mir fehlt leider jeglicher Ansatz dafür!

Gruss Alf


FinnO - Di 19.10.10 23:07

Moin,

na ja, ich vermute mal, dass die Eigenschaften Top und Left eines Buttons sich auf sein parent beziehen. Und wenn du den Button dann als Property deiner Komponente hast und den im Constructor deiner Komponente das Handle ebendieser als Parent festlegst, könnte das etwas werden. Aber gemacht hab ich das auch noch nie, jaenicke hat da auch mit Sicherheit mehr Überblick.

LG


bummi - Di 19.10.10 23:39

Du könntest von TCustomPanel ableiten...

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
  TMyGauge = class(TCustomPanel)
    private
    ....
    protected
    procedure Paint(var Message: TWMPaint); Message WM_PAINT;
    ....


hier im Contructor deine Buttons erzeugen, die Getter/Setter/Events durchschleifen
und im Paint malen


ALF - Mi 20.10.10 01:31

Naja, so richtig schnall ich das nicht wie ich eine 2te Komponente einbinden soll, unabhängig davon, ob ich es versuche in meiner TGaugeCaption zumachen, geschweige, wie ich es machen soll wenn ich 2 komponenten zusammen bringen soll. Bin einfach zu blöd.
Ich hab zwar versucht zu verstehen was Ihr mir geschrieben habt, aber ....

Gruss ALf


jaenicke - Mi 20.10.10 06:09

Ich schreib dir ne kleine Demo... Aber dazu komme ich erst nach der Arbeit heute Abend. ;-)

Wegen der Elternkomponente: Da du ein Fensterhandle brauchst um andere Komponenten mit SetParent auf deine Komponente zu pflanzen bietet es sich an von TCustomControl abzuleiten.

Von Nachfahren wie TCustomPanel abzuleiten ist weniger sinnvoll, da du deren Funktionalität wahrscheinlich gar nicht brauchst. Man sollte immer die höchste Komponente in der Hierarchie wählen, die alles hat was man braucht.

Und TGraphicControl ist für eigene Komponenten, die kein eigenes Fensterhandle brauchen.


Xion - Mi 20.10.10 10:22

Ich mach sowas immer anders. In etwa so:


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:
type TOnBtnClickEvent= procedure (GaugeMax, GaugeMin, GaugePos: integer) of object;

type TGaugeButton=class
  private
    Btn: TButton;
    Gauge: TGauge;

    VOnBtnClick: TOnBtnClickEvent;
    VLeft: integer;

    procedure SetLeft (v: integer);

    procedure InternalOnBtnClick(Sender: TObject);
  public
    constructor Create( Owner: TWinControl);
    property Left: integer read VLeft write SetLeft; 
    property OnBtnClick: TOnBtnClickEvent read VOnBtnClick write VOnBtnClick;
end;

...

constructor TGaugeButton.Create (Owner: TWinControl);
begin
  Gauge:=TGauge.Create(Owner);
  Gauge.Parent:=Owner;

  Btn:=TButton.Create(Owner;
  Btn.Parent:=Owner;
  Btn.OnClick:=InternalOnBtnClick;

  //eventuell noch default positionen setzen
end;

procedure TGaugeButton.InternalOnBtnClick(Sender: TObject);
begin
  if Assigned(OnBtnClick) then
     VOnBtnClick( Gauge.Min, Gauge.Max, Gauge.Pos);
end;

procedure TGaugeButton.SetLeft (v: integer);
begin
  Gauge.Left:=v;
  Btn.Left:=v+5;
end;



Zum verwenden:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
GBtn: TGaugeButton;
begin
  GBtn:= TGaugeButton.Create(Form1);
  GBtn.Left:=100;
  GBtn.OnBtnClick := OnGaugeBtnClick;
end;

procedure TForm1.OnGaugeBtnClick(GaugeMax, GaugeMin, GaugePos: integer);
begin
  ShowMessage('Gauge filled at '+floattostr( (GaugeMax-GaugeMin)/(GaugePos-GaugeMin)*100 )+'%');
end;


Bin mehr ein Fan von Delegation anstatt von Vererbung.

Wenn du jetzt den Button direkt ansprechen willst, dann würde ich ihn public machen. Wenn du ihn dann von Hand verschiebst, bist du selbst Schuld :P


jaenicke - Mi 20.10.10 11:39

Naja, aber ich denke mal die bessere Lösung ist schon eine eigene echte Komponente. Delegation ist schön und gut, aber auch nur dann, wenn es dazu passt. Aber hier...


ALF - Mi 20.10.10 16:47

Hi, es ist ja nicht so, das ich nun von euch erwarte das Ihr mir den code liefert :wink:
Bin natürlich selbst schon dabei was zu machen, geht aber halt langsam vorran. Mal sehen ob ichs packe!?

Achso, ich vergass zu erwähnen, das ich die Komponente die ich aus dem Inet habe einbinden möchte, also nicht die TGauge von Delhpi. Diese hat auch ein onClickereignis und noch ander dabei. So als Randbemerkung
Gruss ALf


Xion - Mi 20.10.10 22:02

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Hi, es ist ja nicht so, das ich nun von euch erwarte das Ihr mir den code liefert :wink:

Das hat ja auch keiner behauptet ;) Ich hab dir nur als Konzept gezeigt, wie ich sowas machen würde. Aber ich bin mal gespannt wie user profile iconjaenicke das vor hat, vielleicht find ich Vererbung dann doch endlich mal ganz nett :mrgreen: Wie es aber ohne Delegation und ohne Mehrfachvererbung gehen soll ist mir ehrlich gesagt ein Rätsel, aber ich bin ja lernfähig :D


jaenicke - Mi 20.10.10 22:04

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
Wie es aber ohne Delegation und ohne Mehrfachvererbung gehen soll ist mir ehrlich gesagt ein Rätsel
Ich habe nicht gesagt ganz ohne Delegation. ;-)

Ja, ich mach mich gleich dran.


ALF - Mi 20.10.10 22:18

jo, ich hab nun auch schon was versucht, natürlich zwecklos. Hab zwar ne neue Komponente die keine Fehler mehr wirft, aber keine Buttons zu sehen. Klar, ohne Grundlage wie sowas funct kann es nur nach hinten losgehen :? :cry:

Edit:
Hab mal den Vorschlag von user profile iconXion umgesetzt. Ist ja toll 8) , währe jetzt nur schön, wenn ich die neue Komponente auch so auf die Form legen könnte. Aber ich code erst mal an das weiter :wink:

Gruss Alf


jaenicke - Do 21.10.10 06:05

So, bin gestern eingeschlafen, habs aber gerade eben schnell gemacht, siehe Anhang.


bummi - Do 21.10.10 08:05

Wahrscheinlich wird die Info wie Clicks durchzuschleifen sind auch noch benötigt, einer der Wege liegt bei (davon ausgehend daß man onKlick intern auch was tun möchte):

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:
....
   property Position: Integer read FPosition write SetPosition;
  Property OnButtonClick:TnotifyEvent read FButtonClick write FButtonClick;
  end;

  TForm259 = class(TForm)
    TrackBar1: TTrackBar;
    procedure TrackBar1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyGauge: TMyGauge;
  public
    { Public declarations }
  end;

var
  Form259: TForm259;

implementation

{$R *.dfm}

{ TMyGauge }


Procedure TMyGauge.InternalClick(Sender:TObject);
begin
// hier gegf. interne Routinen
    if Assigned(FButtonClick) then FButtonClick(Sender);
end;

constructor TMyGauge.Create(AOwner: TComponent);
begin
  inherited;
  DoubleBuffered := True;
  FButton := TButton.Create(Self);
  FButton.OnClick := InternalClick;
  FButton.Left := 10;
 
....


Xion - Do 21.10.10 19:38

Sorry, aber das überzeugt mich nicht ;)

Das kann man weder von Hand auf der Form platzieren (ok, anderes Kapitel), noch kann ich den Button direkt ansprechen. Das einzige ist, dass du deine TGauge nochmals implementierst (im .Paint z.B.), was man ja aus OOP Sicht nun eben nicht braucht (ok, es sieht hübscher aus ;) ).

Was genau ist hier der Vorteil gegenüber meinem? Weil dass ich die Gauge nochmal programmieren muss, find ich deutlich als Nachteil. Nach diesem Schema müsste ich ja auch in allen meinen Klassen (Btn+Gauge, Edit+Gauge, Label+Gauge) immer die Gauge neu schreiben und abändern.
Was ich überhaupt *mist*e find ist, dass ich die ganzen Funktionen nach außen ziehen muss. Hmm, vielleicht wäre es am besten, die Klasse von TButton abzuleiten. Ja, ich glaub das wäre eine gute Idee bei der sogar ich Vererbung nutzen würde :mrgreen:

...außerdem flackert der Button trotz DoubleBuffered:=True...komisch.

Edit:
Vererbung von TButton


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:
type TGaugeBtn = class(TButton)
  private
    Gauge: TGauge;
  
    procedure SetPosition(V: integer);
    function GetPosition: integer;
 
    constructor Create( Owner: TWinControl );
    destructor  Destroy;
  public
    property Position: integer read GetPosition write SetPosition;
end;

constructor TGaugeBtn.Create( Owner: TWinControl );
begin
  inherited Create(Owner);

  Gauge:=TGauge.Create(Owner);
  Gauge.Left:=Self.Left;
  Gauge.Top:=Self.Top+Self.Height;
  Gauge.Width:=Self.Width;
  Gauge.Height:=Self.Height;
  Gauge.Parent:=Owner;
end;

destructor TGaugeBtn.Destroy;
begin
  Gauge.Free;
  Gauge:=nil;

  inherited Destroy;
end;

procedure TGaugeBtn.SetPosition(V: integer);
begin
  Gauge.Progress:=V;
end;

function TGaugeBtn.GetPosition:integer;
begin
  Result:=Gauge.Progress;
end;


Edit:
Getestet und für gut befunden. Wie man das jetzt in den OI kriegt...vermutlich nicht so ohne weiteres (die Komponente muss sich dann irgendwo im Delphi registrieren).
Damit die Klasse gut funktioniert, muss man natürlich bei Left/Top... Veränderungen noch die entsprechenden Funktionen überschreiben um die Gauge mitzubewegen.


Xion - Do 21.10.10 20:01

Funktion zum Packen in den Objektinspektor:
delphi.about.com [http://delphi.about.com/library/bluc/text/uc092501b.htm] hat folgendes geschrieben:
  RegisterComponents( [ComponentClass], 'Tab name');

ACHTUNG: Vorher informieren, was das bewirkt *keine Ahnung von hat*

//Edit: verdammter LoadBalancer...haut mich raus während ich im inline-Editor wurstel. Deswegen der Doppelpost (war wohl mein Fehler nach dem Durcheinander)


jaenicke - Do 21.10.10 20:10

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
ACHTUNG: Vorher informieren, was das bewirkt *keine Ahnung von hat*
Das gehört in die Prozedur Register. Die wird beim Laden des Packages dann ausgeführt. Und da ich die Eigenschaften published gemacht habe, sind die dann auch im OI sichtbar.

Allerdings halte ich davon nicht so viel, deshalb habe ich es nicht hinzugefügt. Ich finde es sehr viel einfacher neue Komponenten einfach manuell zu erstellen. Dann kann jeder das Projekt einfach laden und kompilieren.

Ich hasse es, wenn ich für ein Projekt erst eine halbe Stunde Komponenten installieren darf... :roll:


Jakob_Ullmann - Do 21.10.10 20:10

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
ACHTUNG: Vorher informieren, was das bewirkt *keine Ahnung von hat*


Das sollte der Komponentenwizard eig. automatisch erzeugen. Zumindest war das bei Delphi 7 so. Und das hat was mit der Palette, nicht mit dem OI zu tun. Was da rein soll, musst doch in published als property deklarieren.


Xion - Do 21.10.10 20:19

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Ich hasse es, wenn ich für ein Projekt erst eine halbe Stunde Komponenten installieren darf... :roll:

Absolut auch meine Meinung...nur user profile iconALF wollte wissen wie es geht ;)

user profile iconJakob_Ullmann hat folgendes geschrieben Zum zitierten Posting springen:
Und das hat was mit der Palette, nicht mit dem OI zu tun. Was da rein soll, musst doch in published als property deklarieren.

Das ist natürlich richtig, hatte mit Objekt Inspektor zusätzlich die Palette gemeint :oops:


ALF - Fr 22.10.10 19:55

Ich melde mich mal kurz, das ich noch am Ball bin. Leider bin ich beruflich stark gebunden und kann alle eure Info und Meinungen noch nicht in Ruhe abarbeiten.
Richtig ist, es geht mir nur darum ob es möglich ist, eine neue Komponente zu erstellen die zwei unterschiedliche Komponenten beeinhaltet und dabei all ihre flexibilität behalten.

Habt ihr mir ja schon aufgezeigt :wink:

Leider bin ich ein Typ, der es lieber so hat, das man die neue Komponente auf die Form packen kann, um sie sich anzuschauen, dan zu sehen was ich alles mit ihr machen kann bzw was mir der OI alles für Möglichkeiten bietet.(Also sie dynamisch zu erstellen um sie dann dynamisch einzubinden ist nicht so mein ding) Dabei geht es mir auch nicht darum eine Komponente zu regestrieren, sondern sie in die Pallete mit aufzunehmen.(Wie das funktioniert ist ja bekannt) :wink:

Habt also Nachsicht, das ich noch keine Rückmeldung, wie weit ich mit euren Hinweisen gekommen bin abgeben kann.
Fragen meinerseit, werden auf alle Fälle folgen :mrgreen:

Gruss ALf


ALF - Mo 25.10.10 15:00

So nun habe ich meine Komponenete mal erstellt.
Buttons sind drauf, lassen sich in grösse verändern, Caption kann geschrieben werden, Font kann extra eingestellt werden!(Leider wird die Font Änderung nicht automatisch aktuallisiert sondern erst beim Schreiben von Caption bzw wenn sich an der Gauge was ändert) :cry:

Was nun wichtig währe, ist das Oncklickereignis von den Buttons. Mein Versuch:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Btn1.OnClick:= SetBtnClick;
Btn2.OnClick:= SetBtnClick;
....
....
procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
   //if Tbutton(sender). then
   FBtnClick:= TButton(Sender).Name;
    
end;
es in etwa so umzusetzen, .... naja nix sag dazu.
Ich gehe mal davon aus was user profile iconjaenicke gesagt hat, ohne Handle keine Antwort, trifft in diesem Fall zu und ich kann es also so nicht umsetzen :cry:
Was natürlich das einfachste währe. Es sei, es hat noch jemand eine Idee!!

Gruss ALf


Xion - Mo 25.10.10 15:29

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Leider wird die Font Änderung nicht automatisch aktuallisiert sondern erst beim Schreiben von Caption bzw wenn sich an der Gauge was ändert :cry:

Das kannst du aber von Hand machen im SetFont().

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:

Ich gehe mal davon aus was user profile iconjaenicke gesagt hat, ohne Handle keine Antwort, trifft in diesem Fall zu und ich kann es also so nicht umsetzen :cry:

Das ist deine Komponente, hier bist du Gott! "Alles" geht.

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
   //if Tbutton(sender). then
   FBtnClick:= TButton(Sender).Name;      
end;


Und mit "Alles" meine ich nicht, einem Event einen String zuweisen :P

Ich versteh nicht so ganz was du willst. Ich rate mal:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
   if Assigned(FBtnClick) then
     FBtnClick( Sender );      
end;

So wird der Button nach außen gereicht. DORT kannst du dann TButton(Sender).Name abfragen.

Die noble Variante ist die:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
type TWelcherButtonWurdeGeklickt = (bcButton1WurdeGeklickt ,bcButton2WurdeGeklickt, bcBugInKomponente );
//...
procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
   if Assigned(FBtnClick) then
     begin
        if Btn1=Sender then
          FBtnClick( bcButton1WurdeGeklickt )
        else if Btn2=Sender then
          FBtnClick( bcButton2WurdeGeklickt )
        else
          FBtnClick( bcBugInKomponente)
      end;  
end;

Der Übergabeparameter von FBtnClick muss natürlich dann vom Typ TWelcherButtonWurdeGeklickt sein!


jaenicke - Mo 25.10.10 15:31

Du kannst auch einfach eine Property einführen OnButton1Click oder so, deren Setter dann die Property bei Button1 setzt und deren Getter die wieder dort ausliest. ;-)


ALF - Mo 25.10.10 16:58

mh.. der erste Vorschlag will nicht funcen. Irgenwas mache ich Falsch

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:
unit GaugeButton;

interface

uses
    SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, StdCtrls, StrUtils, Gauges;

type
   TGaugeKind = (gkHorizontalBar, gkVerticalBar);
   TGaugeButton = class(TCustomControl)

   private
       FBtnClick: TObject;
       ....
       ....
   protected
       ....
   public
       ....
       procedure SetBtnClick(Sender: TObject);
   published
       ...
end;

implementation

....
....

constructor TGaugeButton.Create (AOwner: TComponent);
begin
   inherited Create(AOwner);
   ... //<-- hier alles was für die Gauge wichtig ist
   ...
   
   FBtnFont:= TFont.Create; //<-- für die button

   Btn1:= TButton.Create(Self);
   Btn1.Parent:= Self;
   Btn1.Name:= 'Btn1';
   Btn1.OnClick:= SetBtnClick; //<-- zuweisung an die procedure
   Btn1.Font:= FBtnFont;
   ....
   ....
end;

procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
  if Assigned(FBtnClick) then
    FBtnClick:= (Sender);

end;
ich sehe zwar zur Laufzeit SetBtnClick aber auslesen wie oben gezeigt ????

Die anderen Vorschläge sind auch nicht ohne :wink: aber wenn ich das eine nicht mal packe, wie soll es bei den anderen gehen :gruebel:

Gruss ALf


jaenicke - Mo 25.10.10 17:21

Ich schreibe gleich zu Hause ein Beispiel dazu...


ALF - Mo 25.10.10 17:33

neeeee!!! alles zurück Ein grosser Denkfehler von mir
Die einzelnen buttons müssen zur Laufzeit so erkannt werden z.B.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
TForm1.GaugeButton1Click(Sender: TObjekt);
begin
  If TGaugeButton(Sender).Name = 'ButtonGauge1' then
  begin
     if FBtnClick = ??? then
       //btn1 würde geklickt mache was
     else
       //btn2 wurde geklickt mache was anderes 
  end
  else
  If TGaugeButton(Sender).Name = 'ButtonGauge2' then
  begin
     if FBtnClick = ??? then
       //btn1 würde geklickt mache was
     else
       //btn2 wurde geklickt mache was anderes 
  end
   

end;
Alles ander hätte ja keinen Sinn und müsste sehr kompliziert ausgelesenen und verglichen werden!
Da ich ja wissen muss von welcher Gauge der zusätzlich Button geklickt wurde.

Gruss ALf


Xion - Mo 25.10.10 18:02

Du machst das irgendwie komisch...FBtnClick ist ja ein TObject :shock:

Wie genau kommst du denn in deinem Code in Form1 an FBtnClick?? Ist das ne globale Variable?

Guck dir am besten nochmal das Beispiel von Seite 1 von mir an:
user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:


Delphi-Quelltext
1:
type TOnBtnClickEvent= procedure (GaugeMax, GaugeMin, GaugePos: integer) of object;                    


Dort kannst du natürlich statt GaugeMax usw. auch den Button angeben (als Nummer oder Boolean oder wie oben mit TWelcherButtonWurdeGeklickt )


ALF - Mo 25.10.10 18:25

Irgendwie will das alles nicht.
Ich muss doch die zusätzlichen Button, in diesem Fall Btn1 + Btn2 ihr Oncklickergeignis neu zuweisen. Inerhalb meiner neuen Komponente. Die Komponente befindet sich in der Delphi Palette!
Mit dieser Zuweisung muss aber auch zur Laufzeit gearbeitet werden. Das heist, klick ich Btn1 an, muss ich als Rückmeldung die Info abfragen können aus welcher Gauge welcher zusätzliche Button geklickt wurde.
Ich bekomme aber nicht mal ne Info, ob der extra Button überhaupt geklickt wurde :gruebel:

FBtnClick ist ein Fehler, soll setBtnClick sein.

Edit: Habs geschafft :dance:
Ist vielleicht nicht die beste Lösung aber sie funct


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
public
    property BtnClick: String read FBtnClick;

procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
   if TButton(sender).Name = 'Btn1' then
      FBtnClick:= 'Btn1'
   else
     FBtnClick:= 'Btn2';

   onclick(self);
end;


Thx an alle für die Ausdauer mit mir
Gruss ALf


Xion - Mo 25.10.10 22:40


Xions Lösung Deluxe
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:
type TButtonClicked = (btButton1, btButton2);

type TGaugeButtonClickEvent=procedure (Sender: TObject; Clicked: TButtonClicked) of object;

type TGaugeButton=class
//...
  private
     VOnButtonClick : TGaugeButtonClickEvent;
//...
     procedure ButtonClick(Sender: TObject);
  public
     property OnButtonClick: TGaugeButtonClickEvent read VOnButtonClick write VOnButtonCLick;
//...
end;

//...

constructor TGaugeButton.Create(...);
begin
  //...
  Btn1.OnClick:=ButtonClick;
  Btn2.OnClick:=ButtonClick;
  //...
end;

procedure TGaugeButton.ButtonClick (Sender: TObject);
begin
  if Assigned( VOnButtonClick) then
    begin
      if Sender=Btn1 then
         VOnButtonClick( Self, btButton1 ) 
      else if Sender=Btn2 then
         VOnButtonClick( Self, btButton2 );
    end;
end;

//...


Benutzung
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:
type TForm1=class
//...
   procedure GaugeButtonClick( Sender: TObject; Clicked: TButtonClicked);    

//...
GaugeButton1.OnButtonClick:= GaugeButtonClick;
GaugeButton2.OnButtonClick:= GaugeButtonClick;
//...

procedure TForm1.GaugeButtonClick (Sender: TObject; Clicked: TButtonClicked);
begin
  if Sender = GaugeButton1 then
     begin
       if Clicked=btButton1 then
         ShowMessage('Gauge1 - Button1 clicked')
       else if Clicked=btButton2 then
         ShowMessage('Gauge1 - Button2 clicked');
     end
  else if Sender = GaugeButton2 then
     begin
       if Clicked=btButton1 then
         ShowMessage('Gauge2 - Button1 clicked')
       else if Clicked=btButton2 then
         ShowMessage('Gauge2 - Button2 clicked');
     end
end;


ALF - Mo 25.10.10 23:09

Jo, schau ich mir an!
Gibt aber dennoch ein Problem bei meiner Variante :autsch:
Vielleicht auch bei euren, weiss ich noch nicht!

Klicke ich nun einen extra Button, wird die Variable BtnClick gesetzt und das onclick von der Gauge wird aufgrufen.
Wenn ich nur auf die Gauge klicke, bleibt die Variable BtnClick bestehen, sie muss aber dann leer sein!!

Wie nun, daran hab ich nicht gedacht :autsch: was wie nun :cry:

EDIT:
@Xion, hab ich übernommen ist natürlich sauberer als meins, aber.... ganau das gleiche Problem, wenn ich nun nur auf die Gauge klicke, muss ich also das OnClick auch noch aufrufen!

Gruss ALf


jaenicke - Di 26.10.10 05:28

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
@Xion, hab ich übernommen ist natürlich sauberer als meins, aber.... ganau das gleiche Problem, wenn ich nun nur auf die Gauge klicke, muss ich also das OnClick auch noch aufrufen
Ja, wenn du das willst, klar. Geht ja genauso.


Xion - Di 26.10.10 08:15

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Klicke ich nun einen extra Button, wird die Variable BtnClick gesetzt und das onclick von der Gauge wird aufgrufen.
Wenn ich nur auf die Gauge klicke, bleibt die Variable BtnClick bestehen, sie muss aber dann leer sein!!


Auch wenn mir diese Vorgehensweise seltsam vorkommt: Du könntest ja BtnClick nach dem Aufruf des Events ( onclick(self) ) wieder auf nil setzen.

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:

EDIT:
@Xion, hab ich übernommen ist natürlich sauberer als meins, aber.... ganau das gleiche Problem, wenn ich nun nur auf die Gauge klicke, muss ich also das OnClick auch noch aufrufen!


Du weißt einfach Gauge.OnClick das gleiche Event zu und machst dann noch ein if, bei welchem du dann den letzten Parameter z.B. auf btGauge setzt. Alternativ kannst du auch für die Gauge auch ein ganz andres Event machen, welches andere Übergabeparameter hat.


ALF - Di 26.10.10 16:45

Ok, ich hab mir mal erlaubt meine Variante einzusetzen, sagen wir mal so, bin zufrieden :wink:
Ob mann es nun so simpel machen kann, mhhh..., oder doch lieber Profesionel entzieht sich mir, da ja beides funct.
Es sei den, bei mir befinden sich unsauberer Code, der evtl das Prog oder Systhem stört oder anderes?

Hab mal nen kleines Demo und die GaugeButton.pas zum anschauen angehängt. Sollte ich da wirklich schlecht gecodet haben(GaugeButton.pas), dann werd ich selbstverständlich das abändern :mrgreen:

Anbei: Der grösste Teil kommt von der original Gauge.pas aus Delphi
Ne Info, ob ich es so lassen kann, währe nicht schlecht.

Gruss ALf


Xion - Di 26.10.10 17:23

Also nach 15min rumwursteln läufts jetzt erstmal ;) Ich habs ja nicht installiert, musste also erst die dfm lesen und...

1. Funktioniert: ja (scheint zumindest so)

2. Wie ich schon sagte finde ich es sinnlos die Gauge (inbesondre das .Paint) neu zu programmieren...stattdessen von TGauge erben oder Gauge delegieren (d.h. als Instanz wie analog zu den Buttons).

3. Die BtnClick-property find ich immernoch...ungewöhnlich ;) Die damit verbundenen Stringvergleiche find ich aber unschön, weil die CaseSensitive sind, was heißt, du schreibst if 'btn1' ... else ... Dabei ist der ButtonName aber 'Btn1'. Passiert sehr schnell mal und man findet den Fehler nicht.

Edit:
Du hättest die "Danke" ja auch an Postings von uns machen können, die besonders konstruktiv waren :mrgreen: Besonders den Einzeiler von user profile iconjaenicke find ich da irgendwie...amüsant. :P Aber Danke fürs Danke :nut:


ALF - Di 26.10.10 22:42

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
2. Wie ich schon sagte finde ich es sinnlos die Gauge (inbesondre das .Paint) neu zu programmieren...stattdessen von TGauge erben oder Gauge delegieren (d.h. als Instanz wie analog zu den Buttons).
Ja und nein. Ich finds so schöner und, ich weiss was ich gemacht habe! (lerneffekt für mich, so selten wie ich was Code)

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
3. Die BtnClick-property find ich immernoch...ungewöhnlich ;)
Weil man es so nicht macht oder weil es zu simple ist???

user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
Die damit verbundenen Stringvergleiche find ich aber unschön, weil die CaseSensitive sind, was heißt, du schreibst if 'btn1' ... else ... Dabei ist der ButtonName aber 'Btn1'. Passiert sehr schnell mal und man findet den Fehler nicht.
Worauf soll ich aber sonst vergleichen, als auf den Komponenten.Name, den der ist nun mal einmalig. Ich weiss, der vergleich auf 'Btn1' kann immer mal daneben gehn 'btn1'! Solange man btn1 nicht noch mal für was anderes definiert :wink: Aber das sollte jeder Coder wissen! Sonst wüsst ich keine Alternative dazu!

Gruss ALf
ps gab noch danke extra


jaenicke - Di 26.10.10 22:46

Du kannst einfach die Variablen vergleichen.

Delphi-Quelltext
1:
2:
if Sender = Button1 then
  ...


user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Weil man es so nicht macht oder weil es zu simple ist???
Weil es unnötig umständlich ist. Statt dass du den Wert direkt im Event übergeben bekommst, musst du ihn extra aus der Komponente auslesen...


ALF - Mi 27.10.10 18:13

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Du kannst einfach die Variablen vergleichen.
Jo, oft genug gesehen, schon gemacht, wieder vergessen :oops:

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
Weil man es so nicht macht oder weil es zu simple ist???
Weil es unnötig umständlich ist. Statt dass du den Wert direkt im Event übergeben bekommst, musst du ihn extra aus der Komponente auslesen...
Zu sowas, werd ich mal einen extra Thread aufmachen, weil nicht schlüssig die Antwort. Bleiben viele Fragen meinerseits offen!



Ansonsten hab ich user profile iconXion delüx Vorschlag umgesetzt mit einer zusätzlichen Variablen!

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:
type
    TBtnClicked = (bcNoBtn, bcBtn1, bcBtn2);
    TGaugeButtonClickEvent = procedure (Sender: TObject; Clicked: TBtnClicked) of object;
    
    TGaugeButton = class(TCustomControl)    
  private 
    
    FBtnClick : TGaugeButtonClickEvent;
    ....
    ....
    Btn1: TButton;
    Btn2: TButton;
    .... 
    procedure SetBtnClick(Sender: TObject);
    ....

  protected
  .....
  public
  ....
  ....

  published
  ....
  ....
  property OnBtnClick: TGaugeButtonClickEvent read FBtnClick write FBtnCLick;

end;


implementation

.....
.....

constructor TGaugeButton.Create (AOwner: TComponent);
begin
    inherited Create(AOwner);
    OnClick:= SetBtnClick;
    ....
    ....
    
    Btn1:= TButton.Create(Self);
    Btn1.OnClick:= SetBtnClick;
    ....
    ....

    Btn2:= TButton.Create(Self);
    Btn2.OnClick:= SetBtnClick;
    ....
    ....
end;

procedure TGaugeButton.SetBtnClick (Sender: TObject);
begin
    if Assigned(FBtnClick) then
    begin
        if Sender = Btn1 then
           FBtnClick(Self, bcBtn1)  //<--Button1 wurde geklickt
        else
        if Sender = Btn2 then
           FBtnClick(Self, bcBtn2)  //<--Button2 wurde geklickt
        else
           FBtnClick(Self, bcNoBtn);  //<--es wurde in der Gauge geklickt
    end;
end;
Nun geh ich davon aus, das es so richtig umgesetzt ist :wink:

Gruss Alf


ALF - So 31.10.10 15:30

Hilfe meine Routine funct nicht mehr

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:
type
  TBtnClicked = (bcNoBtn, bcBtn1, bcBtn2, bcPlay, bcStop);
  TGaugeButtonClickEvent = procedure (Sender: TObject; Clicked: TBtnClicked) of object;

private
        FBtnClick: TGaugeButtonClickEvent;
        .....
        procedure SetBtnClick(Sender: TObject);
published

  property OnBtnClick: TGaugeButtonClickEvent read FBtnClick write FBtnCLick;

.....
.....

procedure TGaugeButton.SetBtnClick(Sender: TObject);
begin
  if not Assigned(FBtnClick) then //Original war es mal so: if Assigned(FBtnClick) then 
  begin                           // aber da Passiert gar nichts mehr 
    if Sender = Btn1 then
      FBtnClick(Self, bcBtn1)
    else
    if Sender = Btn2 then
      FBtnClick(Self, bcBtn2)
    else
    if TSpeedButton(Sender).Name = 'BtnPLay1' then// hier akzeptiert er komischer weise nicht
    begin                                         //Sender = BtnPlay1, undefinierter Bezeichner??? 
      //if BtnPlay.Glyph.Handle=
      BtnPlay.Glyph.LoadFromResourceName(hInstance, 'Bild4');
      BtnStop.Enabled:= True;
      FBtnClick(Self, bcPlay);

    end
    else
    if TSpeedButton(Sender).Name = 'BtnStop1' then
    begin
       BtnStop.Enabled:= True;
       BtnPlay.Glyph.LoadFromResourceName(hInstance, 'Bild3');
       FBtnClick(Self, bcStop);
    end
    else
    begin
      FBtnClick(Self, bcNoBtn);
      TGaugeButton(Sender).SetFocus;
    end;
  end;
end;

Ändere ich es so, bekomme ich Fehlermeldung an allen stellen wo FBtnClick(Self, ...) steht
Zugriffsverletztung bei Adresse 00000000

Ich habe doch nur was hinzugefügt, zusätzlich 2 Buttons! Selbst wenn ich die wieder rausnehme, bleibt der Fehler!
Gruss ALf


bummi - So 31.10.10 15:54


Delphi-Quelltext
1:
2:
3:
4:
5:
  if not Assigned(FBtnClick) then //Original war es mal so: if Assigned(FBtnClick) then 
  begin                           // aber da Passiert gar nichts mehr 
    if Sender = Btn1 then
      FBtnClick(Self, bcBtn1)
    else


wenn FBtnClick nicht Assigned ist rufst Du es auf??????????

muß heißen if Assigned(FBtnClick)

Du muß die Clickroutine zuweisen, dann passiert auch etwas


ALF - So 31.10.10 16:09

user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
wenn FBtnClick nicht Assigned ist rufst Du es auf??????????

muß heißen if Assigned(FBtnClick)

War ja auch so, nur da überspringt er komischerweise die Abfrage und geht gleich zum ende der Procedure???

user profile iconbummi hat folgendes geschrieben Zum zitierten Posting springen:
Du muß die Clickroutine zuweisen, dann passiert auch etwas

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
OnClick:= SetBtnClick;
....
....
Btn1.OnClick:= SetBtnClick;
Btn2.OnClick:= SetBtnClick;
BtnPlay.OnClick:= SetBtnClick;
BtnStop.OnClick:= SetBtnClick;
sind sie!
Hat ja ach alles funktioniert, nur jetzt nicht mehr!? :cry:

Gruss ALf


jaenicke - So 31.10.10 16:18

user profile iconALF hat folgendes geschrieben Zum zitierten Posting springen:
War ja auch so, nur da überspringt er komischerweise die Abfrage und geht gleich zum ende der Procedure???
Dann ist FBtnClick eben gerade nicht zugewiesen. Das ist ja genau das Problem.

Das ist wie wenn du zum Fahrer sagst:
"Wenn Reifen am Wagen sind, dann fahre los."
Der fährt aber nicht los. Also sagst du einfach:
"Wenn keine Reifen am Wagen sind, dann fahre los."
Und wunderst dich, dass das Auto komische Geräusche macht. (Zugriffsverletzung) :mrgreen:


ALF - So 31.10.10 16:26

Das war ja auch nur zum Test! :wink:
Nur erklärt es mir nicht warum plötzlich nix mehr passiert??? ergo FBtnclick bleibt nil!

Gruss Alf


jaenicke - So 31.10.10 16:26

Du weist OnBtnClick nicht zu. :nixweiss:


ALF - So 31.10.10 17:05

So noch mal neu überarbeitet, alla user profile iconXion delüx Vorschlag! anstatt V steht bei mir F . Trotzdem, nix passiert!?
Was vorher klappte, will jetzt nicht mehr. Ich finde und seh nichts wo es klemmpt :autsch:
:cry: :cry: :cry:

Gruss ALf


bummi - So 31.10.10 17:25

ist 1.) OnBtnClick ein Event im Objektinspektor zugewiesen?
2.) verwendest Du in Deinem Code nur noch VOnButtonClick und nicht noch irgendwo als Leiche FOnButtonClick

wenn ja, wirst Du Komponentensource und Testprogramm als Code hinterlegen müssen damit Dir geholfen werden kann.


ALF - So 31.10.10 17:39

1. ist sichtbar.
2. anstatt V habe ich F genommen!
3. ich hänge mal den Code dran.
und bin gespannt wo ich Dussel nen Fehler nicht mehr sehe!


bummi - So 31.10.10 17:53

Ich musste zum testen die Resoureladerreien rausnehmen und TRoundbutton durch Button ersetzen.

Getestet mit

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.Button2Click(Sender: TObject);
begin
  With TGaugeButton.Create(self) do
    begin
      Parent := Self;
      OnBtnClick := ButtonClick;
    end;
end;


läuft ....

ich bin fast sicher daß Du im Objektinspektor OnBtnClick nicht zugewiesen hast


ALF - So 31.10.10 19:04

Mhh... peinlich!
Mir war so, wenn ich die pas meinem Project hinzufüge und ich sie dann mit Haltpunkte versehe, genau in der OnBtnClick routine, das ich dann zugreifen kan auf den Sender um zu sehen ob alles richtig ausgeführt wird. Das muss dann wohl ein Irrtum von mir gewesen sein!

Alles ander funct. Also im OI doppelclick auf OnBtnClick usw.

Danke noch mal

Gruss ALf


jaenicke - So 31.10.10 19:06

Aller guten Dinge sind drei (also was den Hinweis darauf angeht). :D