Entwickler-Ecke
Datenbanken - TAction VS. ModalResult
Fienix - Di 19.11.19 11:13
Titel: TAction VS. ModalResult
Hallo,
ich hab eine kleines Problem, dass anscheinend eher ein Delphi-Bug zu sein scheint.
Ich hab ein Dataset-Field mit einer OnValidate Prüfung.
wenn ich den Button direkt anklicke, kommt die Exception und ich komme auch nicht weiter.
wenn ich Button.OnClick über TAction über einen ShortCut auslöse, kommt die Exception aber der Dialog schließt sich auch.
Hab ich einen Denkfehler oder ein Bug?
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: 67: 68: 69: 70: 71: 72: 73: 74: 75:
| unit Unit3;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DB, Grids, DBGrids, StdCtrls, DBClient, Mask, DBCtrls, ActnList;
type TForm3 = class(TForm) dataset: TClientDataSet; dbgrd: TDBGrid; ds: TDataSource; dbedtDate: TDBEdit; actlst: TActionList; actFertig: TAction; btnEinfuege: TButton; btnok: TButton; procedure FormCreate(Sender: TObject); procedure actFertigExecute(Sender: TObject); procedure btnEinfuegeClick(Sender: TObject); procedure btnokClick(Sender: TObject); private procedure DoValidate(Sender: TField); public end;
var Form3: TForm3;
implementation
uses DateUtils;
{$R *.dfm}
procedure TForm3.actFertigExecute(Sender: TObject); begin if (self.btnok.enabled) and (self.btnok.visible) then begin btnOK.setfocus; btnOKClick(nil); end; end;
procedure TForm3.btnEinfuegeClick(Sender: TObject); begin dataset.Append;
dataset.FieldByName('Field1').Value := 'Artikel ' + IntToStr(dataset.Recordcount + 1); dataset.FieldByName('Field2').Value := date; dataset.FieldByName('Field3').Value := FormatDatetime('dd.mm.yyyy hh-mm-ss-zzz', now); dataset.Post; end;
procedure TForm3.btnokClick(Sender: TObject); begin Modalresult := mrOk; end;
procedure TForm3.DoValidate(Sender: TField); begin if DaysBetween(date, sender.asDatetime) > 7 then raise Exception.Create('Zu alt'); end;
procedure TForm3.FormCreate(Sender: TObject); begin dataset.CreateDataSet; dataset.FieldByName('Field2').OnValidate := DoValidate; end;
end. |
Im Anhang eine keines Beispiel, damit es leichter nachzuvollziehen ist.
Danke im voraus.
jaenicke - Di 19.11.19 12:22
Das liegt daran, dass du in der Action mit dem Setzen des Fokus die Prüfung auslöst, die OnValidate-Exception dabei abgefangen und dann danach der btnOKClick durchgeführt wird. Deshalb wird dabei das ModalResult wirklich gesetzt und das Fenster geschlossen.
Drückst du auf den Knopf, tritt das Problem auch beim Fokuswechsel auf, aber durch den Fehler wird der Klick, sprich die Methode btnokClick, gar nicht mehr ausgeführt.
Fienix - Di 19.11.19 12:33
Die Exception wird doch nicht abgefangen!
Es wird eine Exception ausgelöst und das Programm läuft trotzdem normal weiter?
jasocul - Di 19.11.19 12:38
Sebastian hat ja schon was zu dem Problem geschrieben.
OnValidate wird ausgelöst, wenn das Feld aktualisiert wird. Das passiert beim Speichern des Datensatzes oder wenn das Eingabefeld den Focus verlieren soll (nicht verliert). Das OnValidate sorgt dafür, dass der Fokus bleibt. Du setzt ihn aber manuell um und bekommst dein Problem.
Fienix hat folgendes geschrieben : |
Hab ich einen Denkfehler oder ein Bug?
|
Es liegt ein konzeptioneller Fehler vor.
Wenn du TAction nutzt, dann weise die Action auch dem Button zu (im Objektinspektor). Das OnClick des Button dann bitte löschen und den entsprechenden Source ins OnExecute der Action verschieben. Ansonsten weißt du irgendwann nicht mehr, was du wo machst.
Außerdem hat TAction auch ein OnUpdate. Damit kannst du z.B. steuern, ob der Button enabled ist.
Da das OnValidate nur in den oben genannten Situationen reagiert, habe ich mich schon lange davon verabschiedet, bzw. eigentlich nich genutzt. Den Aufwand den man dafür treiben muss, damit es an den richtigen Stellen auch reagiert, war mir zu umständlich.
Die Prüfung muss sowieso gemacht werden. In deinem Beispiel kann man die Prüfung einfach auf die Action für den Button verlagern. Wenn das Datum ungültig ist, setzt du den Fokus wieder auf das Eingabefeld, machst ein ShowMessage und setzt keinen ModalResult.
Vorteil: Du hast die volle Kontrolle an der gewünschten Stelle.
Nachteil: Bei vielen Feldern, die geprüft werden müssen, wird der Anwender erst gewarnt, wenn schon viele Felder ausgefüllt sind. Das ist aber durchaus eine übliche Vorgehensweise.
Fienix - Di 19.11.19 12:58
Das funktioniert aber noch weniger.
Wenn die Action dem Button zugewiesen wird, wird ja überhaupt nix mehr geprüft,
deswegen wird ja der Focus explizit auf den Button gesetzt, um die Exception auszulösen.
bei dem SetFocus wird die Exception ausgelöst, ist auch richtig, aber warum läuft das Programm weiter?
Fienix - Di 19.11.19 13:57
grundsätzlich ist das Problem nicht bei der TAction.
sondern beim
SetFocus , anscheinend wird hier die Exception abgefangen :(
hier tritt das gleiche Problem auf:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm3.btnCloseClick(Sender: TObject); begin if (self.btnok.enabled) and (self.btnok.visible) then begin btnOK.setfocus; btnOKClick(nil); end; end; |
jasocul - Di 19.11.19 14:20
Fienix hat folgendes geschrieben : |
Das funktioniert aber noch weniger. |
Weil du konzeptionell falsch damit arbeitest. Das hatte ich versucht oben zu erläutern. Man muss sich entscheiden, ob man TAction verwendet oder nicht. Aber wenn man es nutzt, muss man konsequent sein.
Fienix hat folgendes geschrieben : |
Wenn die Action dem Button zugewiesen wird, wird ja überhaupt nix mehr geprüft, |
Richtig, aber wie schon geschrieben, ist dein Konzept nicht in Ordnung.
Fienix hat folgendes geschrieben : |
deswegen wird ja der Focus explizit auf den Button gesetzt, um die Exception auszulösen. |
OnValidate prüft den Wert, wenn man das DataSet-Feld (nicht das Eingabefeld) verlässt oder ein Post macht. Das hat zu dem Zeitpunkt nichts mit der fokussierten Eingabe-Komponente zu tun. Daher macht dein SetFocus im Source einen Wechsel zum Button und dir geht die Kontrolle darüber verloren. Der Fokus-Wechsel durch Auswahl mit der Maus läuft etwas anders.
Fienix hat folgendes geschrieben : |
bei dem SetFocus wird die Exception ausgelöst, ist auch richtig, aber warum läuft das Programm weiter? |
Weil der Fokus durch deinen Source verändert wird und nicht durch das Event.
Mit OnValidate hat man immer diese Probleme. Deswegen nutze ich es nicht und kontrolliere die Daten z.B. vor einem DataSet.Post.
Es gibt da so eine nettes Ereignis (BeforePost) beim DataSet. Das eignet sich in den meisten Fällen ganz gut. Alternativ kann man das auch in einer Action machen, bevor man das Post auslöst.
jaenicke - Mi 20.11.19 07:18
Fienix hat folgendes geschrieben : |
grundsätzlich ist das Problem nicht bei der TAction.
sondern beim SetFocus , anscheinend wird hier die Exception abgefangen :( |
Da liegt daran, dass die Nachricht WM_SETFOCUS vom System kommt und daher die Exception auch in diese Richtung geschickt wird und nicht "zu dir".
Fienix - Mi 20.11.19 10:12
Ich habs jetzt soweit verstanden, denk ich.
Und das passende Event hab ich auch gefunden:
ds.dataset.UpdateRecord;
damit hab ich genau das richtige um den Ablauf nicht komplett überarbeiten zu müssen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| procedure TForm3.actFertigExecute(Sender: TObject); begin if (self.btnok.enabled) and (self.btnok.visible) then begin ds.dataset.UpdateRecord; btnOK.setfocus; btnOKClick(nil); end; end; |
:beer:
Danke euch für die Hilfe!
jasocul - Mi 20.11.19 13:28
Gern geschehen.
Dennoch solltest du dein Konzept überdenken, wie du mit TAction arbeitest.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!