Default Button
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Default Button
Buttons haben eine property "default". Mir ist aber nicht klar, wie das funktioniert.
Ich war der Meinung, die Onclick Routine des Buttons mit der Property default=true wird automatisch ausgeführt, wenn man die Enter-Taste drückt. Anscheinend ist das aber nicht der Fall, es wird bei Enter die Onclick-Routine von dem Button ausgeführt, der gerade den Focus hat, und das ist ja eigentlich auch in Ordnung.
Bloß, was macht dann die Property "default"?
Ich war der Meinung, die Onclick Routine des Buttons mit der Property default=true wird automatisch ausgeführt, wenn man die Enter-Taste drückt. Anscheinend ist das aber nicht der Fall, es wird bei Enter die Onclick-Routine von dem Button ausgeführt, der gerade den Focus hat, und das ist ja eigentlich auch in Ordnung.
Bloß, was macht dann die Property "default"?
- m.fuchs
- Lazarusforum e. V.
- Beiträge: 2640
- Registriert: Fr 22. Sep 2006, 19:32
- OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
- CPU-Target: x86, x64, arm
- Wohnort: Berlin
- Kontaktdaten:
Re: Default Button
Ja.
Auch ja. Wenn ein anderer Button den Fokus hat, wird dessen OnClick ausgeführt. Wenn aber beispielsweise ein TEdit den Fokus hat, dann wäre die OnClick-Methode des Buttons mit Default dran.
In der Delphi-Hilfe ist das ganz gut beschrieben:
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/StdCtrls_TButton_Default.html hat geschrieben: If Default is true, the button's OnClick event handler executes when the user presses Enter.
Although an application can have more than one Default button, the form calls the OnClick event handler only for the first visible button in the tab order. Moreover, any button that has focus becomes the Default button temporarily; hence, if the user selects another button before pressing Enter, the selected button's OnClick event handler executes instead.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
Ok, danke.
Ich habe jetzt etwas herumprobiert.
Der beste Weg, zu einer intuitiv guten Lösung zu kommen, dürfte sein, zusätzlich zu noch zu setzen - oder jedenfalls die kleinste Taborder von allen Buttons. Dann bekommt kein anderer Button beim Öffnen des Dialogs den Focus.
Ich habe jetzt etwas herumprobiert.
Der beste Weg, zu einer intuitiv guten Lösung zu kommen, dürfte sein, zusätzlich zu
Code: Alles auswählen
default=true
Code: Alles auswählen
TabOrder=0
- fliegermichl
- Lazarusforum e. V.
- Beiträge: 1435
- Registriert: Do 9. Jun 2011, 09:42
- OS, Lazarus, FPC: Lazarus Fixes FPC Stable
- CPU-Target: 32/64Bit
- Wohnort: Echzell
Re: Default Button
Das ist doch aber gar nicht der Sinn. Das erste Edit sollte den Focus bekommen und wenn der Anwender Enter drückt, bekommt er automatisch den default Button bedient. Wie schon weiter oben geschrieben wurde, hat der fokussierte Button ja sowieso Vorrang.
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
Das Problem kommt offenbar daher, dass bei mir die Buttons auf einem eigenen Panel liegen, auf dem Panel befinden sich nur andere Buttons und sonst keine Controls.
Nun hat ein anderes Control, das nicht am Panel liegt, den Focus. Der in der Taborder auf dem Panel liegende erste Button scheint aber irgendwie doch "auf dem Panel" den Focus zu haben, er wird auch so gezeichnet, als hätte er den Focus. Wenn man Enter drückt, wird nicht der auf dem Panel liegende Default Button ausgelöst, sondern dieser Button.
Allerdings, wenn man irgendwo auf das Panel klickt und danach wieder auf ein Control außerhalb des Panels, dann wechselt im Panel der Focus auf den Button, der als Default definiert worden ist, und danch wird bei Enter auch der richtige Button aktiviert. Es handelt sich da offensichtlich um einen Fehler, beim Öffnen eines Formulars bekommt auf einem Panel der Button mit Taborder 0 den Focus (unabhängig davon, dass das Panel den Focus nicht bekommt) und nicht der Default Button.
Nun hat ein anderes Control, das nicht am Panel liegt, den Focus. Der in der Taborder auf dem Panel liegende erste Button scheint aber irgendwie doch "auf dem Panel" den Focus zu haben, er wird auch so gezeichnet, als hätte er den Focus. Wenn man Enter drückt, wird nicht der auf dem Panel liegende Default Button ausgelöst, sondern dieser Button.
Allerdings, wenn man irgendwo auf das Panel klickt und danach wieder auf ein Control außerhalb des Panels, dann wechselt im Panel der Focus auf den Button, der als Default definiert worden ist, und danch wird bei Enter auch der richtige Button aktiviert. Es handelt sich da offensichtlich um einen Fehler, beim Öffnen eines Formulars bekommt auf einem Panel der Button mit Taborder 0 den Focus (unabhängig davon, dass das Panel den Focus nicht bekommt) und nicht der Default Button.
Zuletzt geändert von braunbär am Do 10. Dez 2020, 21:08, insgesamt 1-mal geändert.
Re: Default Button
Die gesamte Tab-Reihenfolge hat nicht mit der Verteilung der Controls auf Container zu tun. Rechts-Klick auf dem Formular > "Tabulatorreihenfolge" > die Checkbox "rekursiv" markieren und den Button darüber klicken, dann wird die Tab-Reihenfolge von links/oben nach rechts/unten eingestellt. Beim Öffen des Formulars hat das Control links oben den Fokus (es sei denn du hast ActiveControl des Formlars verstellt). Wenn nun ein Control fokussiert ist, das die ENTER-Taste selbst nicht verwertet (das wäre z.B. ein Memo, oder eben ein Button), dann wird mit ENTER der Default-Button ausgelöst.
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
Ich habe jetzt ein ganz simples Testprogramm mit Butttons in einem Panel und einem TEdit gemacht. Zu meiner Überraschung funktioniert es Bei diesem Testprogramm richtig.
Jetzt muss ich nur noch herausfinden, warum in meinem Programm beim Öffnen des Formulars der falsche Button den Focus bekommt.
Jetzt muss ich nur noch herausfinden, warum in meinem Programm beim Öffnen des Formulars der falsche Button den Focus bekommt.
Danke für den Hinweis, das ist ein sehr praktisches Feature, das ich bis jetzt noch nicht bemerkt hatte.
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
Es ist mir jetzt gelungen, ein Minimalprogramm zu schreiben, in dem der Fehler auftritt.Es sind zwei Units, das Formular des Hauptprogramms ist leer und ruft nur im FormShow die Dialogroutine auf.
Die Form der Dialogroutine enthält ein leeres Panel für die Buttons und einen Label für die Frage.
Zwei Buttons "Ja" und "Nein" werden im Panel dynamisch erzeugt, der Button "Ja" bekommt die Property Default=true.
Wenn man nach der Dialoganzeige Enter drückt, wird aber der Nein-Button aktiviert.
Windows 10 64bit
Lazarus 2.0.10
FPC 3.2.0
Die Form der Dialogroutine enthält ein leeres Panel für die Buttons und einen Label für die Frage.
Zwei Buttons "Ja" und "Nein" werden im Panel dynamisch erzeugt, der Button "Ja" bekommt die Property Default=true.
Wenn man nach der Dialoganzeige Enter drückt, wird aber der Nein-Button aktiviert.
Windows 10 64bit
Lazarus 2.0.10
FPC 3.2.0
- Dateianhänge
-
- Test.zip
- (2.98 KiB) 79-mal heruntergeladen
Re: Default Button
Es ist offenbar so, dass das erste Control, das in ein Formular eingefügt wird, standardmäßig als ActiveControl genommen wird. Damit ist bei deinem Testprogramm der Nein-Button der aktive und ist fokussiert (d.h. hat auf Windows den blauen Rand). Füge einmal des Labels ein TEdit ein. Damit ist das Edit fokussiert und die Default-Eigenschaft des Ja-Buttons kommt zum Tragen.
Sind nur Buttons und Labels auf dem Formular, musst du also zusätzlich zur Default-Eigenschaft des Ja-Buttons auch dlg.ActiveControl auf den Ja-Button setzen.
Sind nur Buttons und Labels auf dem Formular, musst du also zusätzlich zur Default-Eigenschaft des Ja-Buttons auch dlg.ActiveControl auf den Ja-Button setzen.
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
@wp_xyz
Danke. Nach deinen Erklärungen und einigem weiteren Herumprobieren habe ich jetzt verstanden, wie es funktioniert.
Es ist für meine Aufgabenstellung trotzdem recht lästig, weil die Dialogunit nicht weiß, was der Programmierer als Zusatzcontrol an den Dialog anhängen wird. Wenn nämlich das Zusatzcontrol keine fokussierbaren Komponenten enthält (z.B. nur ein Panel mit Labels mit Erläuterungen), dann hilft es nicht, dem Zusatzcontrol die Taborder 0 zu geben - der erste Button bekommt dann trotzdem den Focus, weil in dem Fall außer den anderen Buttons, die alle in der Tabfolge später kommen, keine fokussierbaren Controls in der Form sind. Und wie schon gesagt, das weiß die Dialogunit im vorhinein nicht.
Wahrscheinlich gibt es keine Möglichkeit, im Formshow vorab herauszufinden, was nach dem FormShow das aktive Control der Form sein wird? Wenn nicht, dann fällt mir als Lösung nur ein, beim ersten OnPaint Ereignis der Form den activecontrol wenn nötig zu korrigieren, zu dem Zeitpunkt sollte der activecontrol ja hoffentlich feststehen.
Danke. Nach deinen Erklärungen und einigem weiteren Herumprobieren habe ich jetzt verstanden, wie es funktioniert.
Es ist für meine Aufgabenstellung trotzdem recht lästig, weil die Dialogunit nicht weiß, was der Programmierer als Zusatzcontrol an den Dialog anhängen wird. Wenn nämlich das Zusatzcontrol keine fokussierbaren Komponenten enthält (z.B. nur ein Panel mit Labels mit Erläuterungen), dann hilft es nicht, dem Zusatzcontrol die Taborder 0 zu geben - der erste Button bekommt dann trotzdem den Focus, weil in dem Fall außer den anderen Buttons, die alle in der Tabfolge später kommen, keine fokussierbaren Controls in der Form sind. Und wie schon gesagt, das weiß die Dialogunit im vorhinein nicht.
Wahrscheinlich gibt es keine Möglichkeit, im Formshow vorab herauszufinden, was nach dem FormShow das aktive Control der Form sein wird? Wenn nicht, dann fällt mir als Lösung nur ein, beim ersten OnPaint Ereignis der Form den activecontrol wenn nötig zu korrigieren, zu dem Zeitpunkt sollte der activecontrol ja hoffentlich feststehen.
Re: Default Button
Du könntest vor dem Einfügen der Buttons alle Controls des Dialogs durchlaufen und prüfen, ob es mindestens ein fokussierbares Control gibt. Falls nein, kannst du unbesorgt den Default-Button zum ActiveControl machen. Etwa so:
Warnung: Dieser Code deckt nicht alle Fälle ab. Wenn zum Beispiel im Dialog ein leeres Panel ist, würde hier CanFocus anspringen und den falschen Rückgabewert von CanFocusAnyControl() bewirken. Also: noch mehr Arbeit nötig, die ich aber dir überlassen möchte.
Code: Alles auswählen
Procedure DlgSetStdButtons;
var
B:TBitBtn;
focusButtons: Boolean;
procedure DlgAddButton;
begin
B:=TBitBtn.Create(dlg.pnlButtons);
B.Parent:=dlg.pnlButtons;
B.Autosize:=true;
B.OnClick:=@dlg.BitBtnClick;
end;
function CanFocusAnyControl: Boolean;
var
i: Integer;
s: String;
begin
for i := 0 to dlg.ComponentCount-1 do
begin
s := TComponent(dlg.Components[i]).Name;
if (dlg.Components[i] is TWinControl) and TWinControl(dlg.Components[i]).CanFocus and
(dlg.Components[i] <> dlg.pnlButtons) then
begin
Result := true;
exit;
end;
end;
result := false;
end;
begin
focusButtons := not CanFocusAnyControl;
DlgAddButton;
B.Caption:='Nein';
B.Tag:=ord(dlgNo);
B.Left:=20;
DlgAddButton;
B.Caption:='Ja';
B.Tag:=ord(dlgYes);
B.Left:=80;
B.Default:=true;
if focusButtons then dlg.ActiveControl := B;
end;
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
Danke für deine Antwort. So ähnlich habe ich es befürchtet.
Jetzt heißt es einmal nachdenken und probieren.
Wahrscheinlich muss ich dann die Controls, wenn sie Parent von anderen Controls sind, rekursiv durchgehen, um zu sehen, ob irgendwas fokussierbar ist. Mal sehen...
Jetzt heißt es einmal nachdenken und probieren.
Wahrscheinlich muss ich dann die Controls, wenn sie Parent von anderen Controls sind, rekursiv durchgehen, um zu sehen, ob irgendwas fokussierbar ist. Mal sehen...
Re: Default Button
Normalerweise nicht. In meinem Beispiel werden die "Components" des Formulars verwendet, das sind die Komponenten, die das Formular als Owner haben. Da dies normalerweise der Fall ist, erreicht die Schleife alle Controls, selbst wenn sie auf mehrere Panels in Panels in Radiogroups usw verteilt sind. - siehe beigefügtes Beispiel.
Ich glaube, das Hauptproblem ist, die Komponenten zu erkennen, die überhaupt fokussierbar sind. Mein Test auf "CanFocus" ist auf jeden fall zu simpel, weil er auch ein Panel erkennt. Bei der Durchsicht der Unit Controls ist mir die Methode GetTabOrderList aufgefallen, in der jeder Container seine Kinder in Tab-Reihenfolge auflistet. Für deine Aufgabe müsste es reichen, diese Liste, die vom Formular aus erzeugt worden ist, zu durchlaufen, bis man das erste Control findet, bei dem TabStop true ist.
- Dateianhänge
-
- form_components.zip
- (2.6 KiB) 78-mal heruntergeladen
-
- Beiträge: 369
- Registriert: Do 8. Jun 2017, 18:21
- OS, Lazarus, FPC: Windows 10 64bit, Lazarus 2.0.10, FPC 3.2.0
- CPU-Target: 64Bit
- Wohnort: Wien
Re: Default Button
Danke dir, auf die Lösung wäre ich nur schwer gekommen, und GetTabOrderList werde ich mir auf jeden Fall merken..
Ich war in der Zwischenzeit auch nicht untätig und habe für mein Problem eine wirklich einfache Lösung gefunden. Ich muss ja nur wissen, ob beim Starten der Formulars der erste von mir erzeute Button den Fokus hat, oder ob irgendein anderes (egal welches) Control das activecontrol ist. Im OnPaint eines Formulars ist activecontrol bereits richtig gesetzt, ich brauche da also gar nicht selbst suchen.
Beim Erzeugen der Buttons merke ich mir, welcher Button als erster erzeugt worden ist, in dlg.DefaultButton speichere ich einen Zeiger auf den Button, der als DefaultButton vorgesehen ist, und dann ist es ganz einfach:
Ich war in der Zwischenzeit auch nicht untätig und habe für mein Problem eine wirklich einfache Lösung gefunden. Ich muss ja nur wissen, ob beim Starten der Formulars der erste von mir erzeute Button den Fokus hat, oder ob irgendein anderes (egal welches) Control das activecontrol ist. Im OnPaint eines Formulars ist activecontrol bereits richtig gesetzt, ich brauche da also gar nicht selbst suchen.
Beim Erzeugen der Buttons merke ich mir, welcher Button als erster erzeugt worden ist, in dlg.DefaultButton speichere ich einen Zeiger auf den Button, der als DefaultButton vorgesehen ist, und dann ist es ganz einfach:
Code: Alles auswählen
procedure TDialog.FormPaint(Sender: TObject);
begin
if FirsttimePaint
then begin
FirstTimePaint:=false;
if (DefaultBtn<>nil) and (ActiveControl=FirstButton)
then ActiveControl:=DefaultBtn;
end;
end;