![]() |
verschränke ToggleBoxes
Hallo zusammen,
folgender Code:
Delphi-Quellcode:
Ich habe zwei ToggleBoxes welche jeweils abwechselnd Checked bzw. nicht Checked sein sollen. Damit nicht der Fall Auftritt, dass beide Checked oder nicht Checked sind, gibt es die if abfragen welche eine solche Kombination verhüten sollen. Ich hatte die Prozeduren ursprünglich im Change- Event, allerdings nicht beachtet, dass OnChange auch bei Veränderungen durch das Programm ausgelöst wird, sodass es mir gelungen ist, eine "doppelt gewundene Endlosschleife" zu programmieren :lol:, welche irgendwann (nach ca. 1/2 sekunde) in einem Zugriffsfehler endet (Warum?). Jetzt wollte ich die Methoden über den Objektinspektor neu zuweisen, sodass jetzt das Klick- Ereignis diese auslöst. Allerdings besteht nach wie vor der Fehler. Kann es sein, dass Lazarus die alten Verbindungen irgendwie nicht gelöscht hat, obwohl ich sie im Objektinspektor entfernt habe?
procedure TForm1.GehevorChange(Sender: TObject);
begin if Gehevor.Checked = false then begin Gehevor.Checked:= true; exit; end; Gehezurueck.Checked:= false; end; procedure TForm1.GehezurueckChange(Sender: TObject); begin if Gehezurueck.Checked = false then begin Gehezurueck.Checked:= true; exit; end; Gehevor.Checked:= false; end; |
AW: verschränke ToggleBoxes
Es ist eher simpel zu lösen:
Erstelle dir eine Eigenschaft vom Typ
Delphi-Quellcode:
mit einer Setter-Methode:
Boolean
Delphi-Quellcode:
procedure TForm1.SetStatus( const Value : Boolean );
begin if FStatus <> Value then begin FStatus := Value; GeheVor.Checked := FStatus; GeheZurueck.Checked := not FStatus; end; end; procedure TForm1.GehevorChange(Sender: TObject); begin SetStatus( GeheVor.Checked ); end; procedure TForm1.GehezurueckChange(Sender: TObject); begin SetStatus( not GeheZurueck.Checked ); end; |
AW: verschränke ToggleBoxes
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:
![]() ![]() Den Begriff ToggleBox scheint es im Zusammenhang mit Delphi nicht wirklich zu geben. Es gibt aber ToggleButtons, z.B. bei den Jedis (siehe Bild 2). |
AW: verschränke ToggleBoxes
@Sir Rufo: Rufst Du innerhalb der Klasse immer explizit den Setter der Eigenschaft auf, oder ist das ein Versehen? Ich würde das nie machen. Der Setter ist eigentlich privater als privat und sollte noch nicht einmal innerhalb der Klasse aufgerufen werden.
|
AW: verschränke ToggleBoxes
Warum ist der Setter privater als privat? Es gibt Sprachen, in denen ist der sogar der einzige Weg auf private Felder zuzugreifen.
Warum sollte man interne Prüfungen auf Wertebereiche, welche im Setter stehen an anderer Stelle wiederholen sollen? |
AW: verschränke ToggleBoxes
Zitat:
@SirRufo: Ich möchte nach Möglichkeit sofort die Änderungsinformation an eine weitere Klasse weiterreichen, deswegen wollte ich kein zusätzliches Feld deklarieren und die gesamte Abfrage innerhalb des OnChange bzw. OnClick durchführen. Was mich immer noch wundert, ist, dass er das Ereignis immer noch als OnChange verwendet, obwohl ich es im Objektinspektor auf OnClick umgestellt habe. |
AW: verschränke ToggleBoxes
Zitat:
|
AW: verschränke ToggleBoxes
Ja, alles ist ordnungsgemäß so eingestellt, dass es funktionieren sollte. :roll: Anscheinend gibt es da einen Bug in der Zuweisung der Events im Objektinspektor oder ich mache irgendwas anderes falsch.
Der Fehler tritt auch in der Form auf, dass er die "Schleife" ein paar mal durchrattert und danach den Fehler eines falschen Zugriffs schmeißt. Wisst ihr, warum er das tut? |
AW: verschränke ToggleBoxes
Zitat:
Dann packe die Logik doch einfach in diese Klasse oder schalte da einfach eine Klasse davor, die diese Logik innehat. Was man nicht macht, ist die Logik in der Form/View so an direkt mit den Controls zu verkoppeln, wie du das gemacht hast. @Dejan Vu Es ging mir nur um das Prinzip, wie man das zusammenfassen kann. Richtig, richtig, wird das z.B. mit einem ViewModel, wo jeder Button seinen Status auslesen und setzen kann und zwar unabhängig voneinander. Das ViewModel kümmert sich nun intern um die entsprechenden logischen Zusammenhänge und übergibt den resultierenden Wert an die Datenklasse oder wohin auch immer. ViewModel:
Delphi-Quellcode:
View:
type
TFooViewModel = class( TViewModelBase ) private FStatus: Boolean; FButton2Checked: Boolean; FButton1Checked: Boolean; procedure SetStatus( const Value: Boolean ); procedure SetButton1Checked( const Value: Boolean ); procedure SetButton2Checked( const Value: Boolean ); function GetButton1Checked: Boolean; function GetButton2Checked: Boolean; public property Button1Checked: Boolean read GetButton1Checked write SetButton1Checked; property Button2Checked: Boolean read GetButton2Checked write SetButton2Checked; end; implementation { TFooViewModel } function TFooViewModel.GetButton1Checked: Boolean; begin Result := FStatus; end; function TFooViewModel.GetButton2Checked: Boolean; begin Result := not FStatus; end; procedure TFooViewModel.SetButton1Checked( const Value: Boolean ); begin SetStatus( Value ); end; procedure TFooViewModel.SetButton2Checked( const Value: Boolean ); begin SetStatus( not Value ); end; procedure TFooViewModel.SetStatus( const Value: Boolean ); begin if FStatus <> Value then begin FStatus := Value; OnPropertyChanged( ['Button1Checked', 'Button2Checked'] ); end; end;
Delphi-Quellcode:
type
TFooViewForm = class( TBaseForm ) Switch1: TSwitch; Switch2: TSwitch; procedure Switch1Switch( Sender: TObject ); procedure Switch2Switch(Sender: TObject); private FViewModel: Ref<TFooViewModel>; protected procedure ViewModelPropertyChanged( Sender: TObject; const e: TPropertyChangedArgs ); override; public procedure SetViewModel( AViewModel: TViewModelBase ); override; end; var FooViewForm: TFooViewForm; implementation {$R *.fmx} { TFooViewForm } procedure TFooViewForm.SetViewModel( AViewModel: TViewModelBase ); begin FViewModel := AViewModel as TFooViewModel; inherited; end; procedure TFooViewForm.Switch1Switch( Sender: TObject ); begin inherited; if FViewModel.IsAssigned then FViewModel.Reference.Button1Checked := Switch1.IsChecked; end; procedure TFooViewForm.Switch2Switch(Sender: TObject); begin inherited; if FViewModel.IsAssigned then FViewModel.Reference.Button2Checked := Switch2.IsChecked; end; procedure TFooViewForm.ViewModelPropertyChanged( Sender: TObject; const e: TPropertyChangedArgs ); begin inherited; if FViewModel.IsAssigned then begin if e.Match( 'Button1Checked' ) then Switch1.IsChecked := FViewModel.Reference.Button1Checked; if e.Match( 'Button2Checked' ) then Switch2.IsChecked := FViewModel.Reference.Button2Checked; end; end; |
AW: verschränke ToggleBoxes
Ich sehe gerade, das im Code die Methode 'SetStatus' als 'Setter' bezeichnet wird, was er ja gar nicht ist, denn wo keine Property, da auch kein Setter. Insofern führt das dann auch mal zu Misverständnissen. Richtig(er) sollte es also sein, eine private Property 'Status' einzuführen. Denn sonst sieht man ja nicht, das 'FStatus' und 'SetStatus' etwas miteinander zu zun haben.
Delphi-Quellcode:
Type
TFoo = class property Status : boolean Read FStatus Write SetStatus; end; Zitat:
Ich kenne keine Programmiersprache, bei der direkte Aufruf des Setters "sogar der einzige Weg" ist, um "auf private Felder zuzugreifen". Zitat:
|
AW: verschränke ToggleBoxes
Zitat:
Im Setter kann auch etwas mehr als "nur" die Zuweisung erfolgen, deshalb kann es auch intern sinnvoll sein den Setter aufzurufen. Will ich sicherstellen, das weitere Contraints erfüllt werden, welche im Setter überprüft werden -> Aufruf Setter Will ich den Wert auf jedenfall setzen -> direkter Zugriff auf Feld. |
AW: verschränke ToggleBoxes
Zitat:
Zitat:
Zitat:
Jetzt mal wieder an die normaldenkende Allgemeinheit (jeder der sich dazu zählt, darf sich angesprochen fühlen) Um aus diesen Toggle-buttons jetzt einen TriState zu machen, ändert man einfach das ViewModel ab (macht natürlich nur Sinn, wenn diese Information im Daten-Modell auch verwertet werden kann, logisch - aber genau das sind ja die Änderungen, die immer wieder auf einen zukommen) und schwups, ohne eine Änderung an der View, passiert alles wie gewünscht:
Delphi-Quellcode:
type
TStatus = ( None, Prev, Next ); TFooViewModel = class( TViewModelBase ) private FStatus: TStatus; FButton2Checked: Boolean; FButton1Checked: Boolean; procedure SetStatus( const Value: TStatus ); procedure SetButton1Checked( const Value: Boolean ); procedure SetButton2Checked( const Value: Boolean ); function GetButton1Checked: Boolean; function GetButton2Checked: Boolean; public property Button1Checked: Boolean read GetButton1Checked write SetButton1Checked; property Button2Checked: Boolean read GetButton2Checked write SetButton2Checked; end; implementation { TFooViewModel } function TFooViewModel.GetButton1Checked: Boolean; begin Result := FStatus = TStatus.Prev; end; function TFooViewModel.GetButton2Checked: Boolean; begin Result := FStatus = TStatus.Next; end; procedure TFooViewModel.SetButton1Checked( const Value: Boolean ); begin if GetButton1Checked <> Value then if Value then SetStatus( TStatus.Prev ) else SetStatus( TStatus.None ); end; procedure TFooViewModel.SetButton2Checked( const Value: Boolean ); begin if GetButton2Checked <> Value then if Value then SetStatus( TStatus.Next ) else SetStatus( TStatus.None ); end; procedure TFooViewModel.SetStatus( const Value: TStatus ); begin if FStatus <> Value then begin FStatus := Value; OnPropertyChanged( ['Button1Checked', 'Button2Checked'] ); end; end; |
AW: verschränke ToggleBoxes
Zitat:
Delphi-Quellcode:
, sollte bei dem ungewünschten Aufruf der folgende Code einfach übergangen werden. Dennoch tritt bei mir der Fall auf, wenn ich zurück wechseln möchte von FStatus:= false zu true, dass dann aus irgend einem Grund (ich habe keine Ahnung, aus Welchem) trotzdem in den if-Block gegangen wird und somit eine weitere Verkettung sozusagen "rekursiver" Aufrufe erfolgt. :gruebel: Hat jemand eine Idee, warum das passiert?
if FStatus <> Value
Im Prinzip könnte ich ja einfach komplett neue Methoden direkt als OnClick anlegen und den alten Code kopieren, aber mich interessiert es zu wissen, warum trotz der neuen Verknüpfung immer noch OnChange aufgerufen wird. |
AW: verschränke ToggleBoxes
Dann zeig doch mal deinen Code denn nur anhand deiner Beschreibung kann man da nichts finden
|
AW: verschränke ToggleBoxes
...Beim Schreiben viel mir gerade der Fehler auf, wo der Fehler lag...war ein einfacher Tippfehler :roll::wall: Der Code funktioniert jetzt prima:thumb:
Allerdings bleibt jetzt dennoch das Grundübel, nämlich, dass beim OnClick gleichzeitig das OnChange mit aufgerufen wird, obwohl ich das im Objektinspektor geändert habe. Das war eigentlich auch meine Ausgangsfrage. Weiß jemand über einen Bug in Lazarus bescheid, bei dem die Verknüpfungen der Events bei Löschen trotzdem bestehen bleiben? |
AW: verschränke ToggleBoxes
Ok, letzte Gelegenheit, dann frag einfach nicht mehr, bzw. ich bin solange raus bis:
Zitat:
|
AW: verschränke ToggleBoxes
Oh.:shock: Dann habe ich mich wohl nicht verständlich ausgedrückt. Der Code funktioniert jetzt wunderbar, alles kein Problem. Ich kann ihn totzdem noch mal posten, auch wenn es nichts anderes ist, als bereits oben schon:
Delphi-Quellcode:
Der Code funktioniert ohne Fehler und tut was er soll, da die if- Verzweigung davor schützt, dass eine "Endlosschleife" entsteht. Im Prinzip ist es also jetzt nur noch ein Schönheitsfehler, bzw, wie ich vermute Bug in Lazarus, den ich aber auch gerne verstehen würde. Ich habe also im Objektinspektor die Property OnChange geleert und OnClick befüllt und schließlich noch von Hand auf Gehevor/zurückClick umbenannt, dann dürfte die Methode doch nicht bei OnChange aufgerufen werden, oder?
procedure TForm1.GehevorClick(Sender: TObject); //Diese Methoden sind beide OnClick, ich habe sie zuvor von OnChange auf OnClick geändert. Allerdings werden sie trotzdem immer noch als OnChange aufgerufen, wenn die Eigenschaft Checked der ToggleBox unten* neu zugewiesen wird.
begin SetRichtung(Gehevor.Checked); end; procedure TForm1.GehezurueckClick(Sender: TObject); begin SetRichtung(not Gehezurueck.Checked); end; procedure TForm1.SetRichtung(const aValue: boolean); begin if FRichtung <> aValue then begin FRichtung:= aValue; Gehevor.Checked:= FRichtung; //*Hier werden wieder die Methoden oben aufgerufen, obwohl diese als OnClick ja nicht aufgerufen werden sollten. Gehezurueck.Checked:= not FRichtung; end; end; |
AW: verschränke ToggleBoxes
Aha, das ist aber ein völlig normales Verhalten und sollte auch so dokumentiert sein. Eine Änderung der Eigenschaft Checked löst auch einen OnClick-Event aus. Es kann auch genauso gut sein, dass beim Setzen der Eigenschaft Checked immer OnClick gefeuert wird und OnChange nur dann kommt, wenn Changed vorher anders war. Genauso gut kann es sogar (auch wenn es falsch wäre -> Bug) sein, dass beide Events immer gefeuert werden, wenn der Eigenschaft Checked ein Wert zugewiesen wird.
Das genaue Verhalten kann man sich im Quelltest der Komponente aber sehr schön anschauen, dann weiß man was da genau passiert. |
AW: verschränke ToggleBoxes
Tatsächlich! OnClick wird auch dann ausgelöst, wenn Checked gesettet wird. Irgendwie bin ich davon ausgegangen, dass OnClick logischerweise nur dann ausgelöst wird, wenn man darauf klickt :wink:. Dann hat ja alles seine Richtigkeit, vielen Dank für die Info:thumb:
Und drauf gekommen, mal im Quellcode nachzuschauen bin ich natürlich auch nicht. :wall: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:35 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz