Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Diverse Fokusprobleme (https://www.delphipraxis.net/180093-diverse-fokusprobleme.html)

himitsu 22. Apr 2014 13:05


Diverse Fokusprobleme
 
Die Suche hat etwas länger gedauert, vorallem da wir bisher dachten der Fehler liegt bei uns (irgendwelchen komischen Dingen in unserem Programm)
und es fiel nicht überall auf, da zufällig bei einigen selbstgebauten Dialogen der Fokus manuell gesetzt wurde.

Das Problem lieg darin, daß öfters der Fokus einfach "verschwindet" und man erst manuell in das Edit/Grid wieder reinklicken muß.


So, aber nun stellte sich raus, daß vermutlich seit den VISTA-Anpassungen, mit den neuen CommonControls, sich im Delphi ein liebevoller Bug versteckt, welcher grob fahrlässig den Fokus zerschießt.
Und (vermutlich) wenn man einen Dialog im OnClose eines Fensters anzeigt, welches dann via caFree erst freigegeben wird, nachdem der Dialog sichtbar wurde, dann dreht alles durch und es passiert schonmal, das gleich die ganze Anwendung den Fokus verliert und im Hintergrund verschwindet.

MDI-Fenster sind für Windows ja mehr Komponenten, als eigenständige "Fenster".


Demo:
- Fokus im Memo
- links den Speedbutton anklicken und Dialog schließen => Fokus bleibt im Memo (siehe Text-Cursor oder einfach mal ein paar Buchstaben eingeben)
- rechts den Speedbutton anklicken => Fokus ist weg (liegt jetzt auf der MainForm)

- bei Fokus auf Button -> nach dem Dialog siehe gepünktelter Fokusrahmen oder einfach mal auf die Leertaste drücken
- Beim Drücken auf einen Button, dann sollte nachher der Fokus beim Button liegen


Grund: Irgendein Trottel Jemand war so schlau, liest vor dem ShowModal das das "für Windows" aktive Fenster aus und gibt ihm nach dem Dialog den Fokus.
Leider ist hier aber nicht die MainForm aktiv, sondern das MDI-Child.

Lösung, bis der Fehler nie repariert wird (da XE garantiert kein Update mehr bekommt): Screen.ActiveControl vorher auslesen und nachher ihm den Fokus zurückgeben.
Oder besser noch nachher
Delphi-Quellcode:
SetFocus(AciveControl.Handle);
(vorher muß man sich nichts speichern), denn die VCL denkt ja immernoch, daß der Fokus wo ganz anders liegt, nämlich dort, wo er auch sein sollte. :stupid:

[edit] Neuer Anhang siehe #5

himitsu 23. Apr 2014 08:59

AW: Fokusproblem (MDI)
 
Und nun auch noch der nächste Schrott: (grade nochmal mit XE6 getestet und seit mindestens XE im Arsch)

TCommonDialog.Execute sucht sich zwar das "richtige" Fenster raus, wo der Dialog drangehängt werden soll (siehe ModalPopupMode), es sei denn man gibt dem Execute das gewünschte Fenster-Handle mit,
aber beim TFindDialog.Execute wird letztendlich das ParentWnd dann doch nicht ausgewertet, stattdessen geht man bescheuerter Weise im TFindDialog.Execute auf EnumThreadWindows und holt sich dort die erste Form, welche gefunden wird (das zuletzt Erstellte), statt den Parameter zu verwenden. :wall:
  • noch kein weiteres Fenster (MDI-Childs werden ignoriert) -> EnumThreadWindows liefert die MainForm (ist ja auch das einzige Fenster) -> MainForm bekommt nachher wieder Fokus
  • ein weiteres (abgedockte) Fenster -> EnumThreadWindows liefert Dieses, selbst wenn es nicht aktiv ist (wurde einfach nur zufällig als Letztes erstellt) -> das Fenster bekommt sofort beim Anzeigen kurz den Fokus und dann danach auch wieder (selbst wenn ein anderes Fenster eigentlich den Fokus hatte)
  • Fenster wieder schließen (hier wurde caFree vergessen, womit das Fenster nur unsichtbar wird) -> wieder wird das externe Fenster gefunden, obwohl es unsichtbar ist -> das unsichtbare Fenster soll nachher den Fokus bekommen und die ganze Anwendung verschwindet im Hintergrund :beifall:

    Wobei das auch beim Standardvorgehen von Delphi knallen sollte, denn da werden ja automatisch alle Fenster erzeugt, selbst wenn sie nicht sofort, sondern erst später sichtbar gemacht werden.
siehe Demo: Neues Fenster > FindText > Dialog zu > Programm weg (am Besten noch irgendein anderes Programm offen haben, wohinter es sich dann verstecken kann)

Und hier auch mal ein Zweizeiler, welcher den Schrott demonstiert.
Delphi-Quellcode:
TMyForm.Create(Self).Close; // DFM: Visible=True
FindDialog1.Execute;


procedure TForm1.Button1Click(Sender: TObject);
begin
  with TForm.Create(Self) do begin
    Show;
    Close;
  end;
  FindDialog1.Execute;
end;



Standardmäßig hängen sich alle weiteren nichtmodalen Forms und die Dialoge an die MainForm und sind auch an Dieser ausgerichtet (stehen immer vor ihr).
Wer das nicht will, muß nur ein bissl was machen:
Application.ModalPopupMode sollte man, vorallem bei Mehrfensteranwendungen besser nicht auf dem Standardwert pmNone stehen lassen
und bei den eigenen Forms, welche nicht von der MainForm aus gestartet werden, die aber nicht an/über der akiven Form hängen sollen ... da sollte man auch noch Form.PopupMode setzen.

Hier wäre es einfacher, wenn man dem TForm.Show, ebenso wie schon beim Dialog.Execute (falls man den Wert nicht ignoriert), einen optionalen Parameter mitgeben könnte, wo man eine explizize Popupform übergibt, damit man das dort hat, wo man es auch braucht.



Gerate nochmal getestet: Delphi 7 = OK / XE und XE6 = futsch


Wer bezahlt uns eigentlich die versenkte Arbeitszeit?
Wir dachten ja meistens, daß es an unserem Programm liegt, weil es ja sonst scheinbar funktioniert, da sich keiner beschwert. (da gingen inzwischen bestimmt schon mehrere Mann-Wochen drauf, in den letzten knapp 3 Jahren)
Und es dauert bestimmt noch paar Stündchen, bis ich alle Delphi-Bugs erfasst hab (jetzt, wo ich endlich weiß, wo man suchen muß) und dann dürfen die sich über 'nen Anruf vom Cheffe freuen.

[edit] Neuer Anhang siehe #5

Daniel 23. Apr 2014 09:11

AW: Fokusproblem (MDI)
 
Kannst Du Deinen Beitrag noch etwas raffen? Zwischen all dem Geschimpfe gehen die (vermutlich) guten Lösungen hoffnungslos unter.

himitsu 23. Apr 2014 09:14

AW: Fokusproblem (MDI)
 
Einen bekannten Fehler bin ich grade noch am Suchen, der sich bei den Kunden auch nett auswirkt ... danach würde ich die Fehler dann nochmal zusammengefassen.

Das mit dem Suchen-dialog fällt uns seit knapp einem halben jahr auf die Füsse, wobei wir nie rausfanden, warum es in manchen Fenstern ging und in Anderen nicht.
Es war auch bisher nie Zeit da jetzt tagelang zu suchen, bis ich am Donnerstag mal wieder Zeit hatte und dann zufällg drüber gestolpert bin.

Seit der Umstellung von D7 auf XE suche ich jetzt derartige Fehler und das ist auch einer der Hauptgründe, warum wir da immernoch auf XE festhängen, weil wir uns nicht trauten das komplette und jahrzehnte alte Projekt weiter hochzuportieren (wer weiß was dann zusätzlich auch nicht läuft).

himitsu 23. Apr 2014 11:37

AW: Diverse Fokusprobleme
 
Liste der Anhänge anzeigen (Anzahl: 1)
falscher Fokus von Delphi gemerkt und gesetzt, bei neuen Common-Dialogen
  • Fokus in einem MDI-Fenster (z.B. auf Edit oder Button)
  • neuen Common-Dialog anzeigen (z.B. MessageDlg)
  • nach dem Schließen sagt Screen.ActiveForm und Screen.ActiveControl der Fokus sei richtig
  • aber in Wirklichkeit ist der Fokus direkt bei beim MDI-Parent (die HauptForm in der Demo)
Grund: TCustomTaskDialog.DoExecute wird am ende der Fokus auf die Top-Level-Form gesetzt und nicht auf die eingebettete MDI-Form, bzw. nicht auf das aktive Control.

Demo: Fokus ins Memo -> SpeedButton: New CommonDialog -> OK -> Fokus landet beim MDI-Parent (egal ob mit externem Fenster "New Form" oder nicht)


Fokus falsch von Delphi gesetzt, bei Dialogen
  • externe Form erstellen (New Form)
  • Fokus in Hauptform, älterer Form oder MDI
  • FindDialog anzeigen und wieder schließen
  • Fokus landet bei zuletzt erstellter Form (egal ob die Sichtbar ist oder nicht)
  • Wenn Ziel-Form nicht sichtbar, dann verliert das ganze Programm den Fokus und verschwindet im Hintergrund
Grund: Der gewünschte Ziel-Fenster wird ignoriert. (per ParentWnd-Parameter übergeben, oder richtig über TCommonDialog.Execute bestimmt)
Stattdessen wird in TFindDialog.Execute(ParentWnd) über EnumThreadWindows "irgendein" zuletzt erstelltes Fenster genommen, welches auch unsichtbar oder deaktivert sein kann. (wird garnicht geprüft)

Demo 1: New Form -> FindText -> Abbrechen -> Fokus bei falscher Form
Demo 2: New Form -> pmAuto1 schließen -> FindText -> Abbrechen -> Fokus ist komplett weg


Zerstörung der PopupParent-Beziehung beim Schließen einer eingebetteten Form
  • nicht-modale Form hängt via PopupMode=pmExplicit und PopupParent=MainForm immer über der Hauptform
  • weitere nichtmodale Form wird erstellt
  • PopupParent-Verbindung wird manchmal getrennt und die erste Form kann hinter der Hauptform verschwinden
Grund: noch unbekannt


Fokus-Setzten beim Schließen von Fenstern -> Fokus darf nicht umgesetzt werden, wenn er manuell wo anders hingelegt wurde
  • neuer Fokus wird ignoriert -> Delphi setzt den Fokus dennoch auf die Form, wo es denkt da solle er hin. (bei modaler Fenstern ... bei nichtmodal weiß ich es jetzt grade nicht)
    Delphi-Quellcode:
    AndereForm.SetFocus;
    Close;
  • über die WinAPI funktioniert es komischer Weise aber dennoch
    Delphi-Quellcode:
    Windows.SetFocus(AndereForm.Handle);
    Close;
  • Ein SetFocus, egal ob Delphi oder WinAPI, nach dem TForm.Close ausgeführt, wird immer ignoriert, bzw. wieder überschrieben. (nur via WinAPI davor geht es)
Grund: keine Ahnung (hab nicht rausgefunden ob und wo da der Fokus gesetzt wird)

Demo 1: New Form -> New Form -> Fokus auf MainForm/MDIForm -> pmAuto1 oder pmAuto2 schließen -> Fokus bei der anderen pmAuto* und nicht in der MainForm
Demo 2: New Form -> New Form -> New Form -> Fokus auf irgendein pmAuto* -> ein anderes pmAuto* schließen -> Fokus ist beim richtigen Fenster


__________________________________________________ ____________________________________________


Sobald das Zielfenster nicht sichtbar ist, und es wird dennoch versucht den Fokus da hinzusetzen, dann verliert das Programm den Focus.
Das wird halt über Windows.SetFocus gemacht, ohne den Rückgabewert auszuwerten, und nicht über TControl.SetFocus. (TControl.SetFocus wirft ja eine Exception wenn das Fenster/Control nicht fokusierbar ist)


Wenn beim Fokuszurücksetzen seitens Delphi etwas total schief lief und dadurch das Programm im Hintergrund verschwindet, dann ist oftmals Screen.ActiveForm/Screen.ActiveCustomForm = nil.
Selbst wenn das Programm absichtlich im Hintergrund landet, da ein anderes Programm den Fokus bekommt, dann gibt es eigentlich nie ein nil dort.
Derartige Fehler konnten wir "beheben", indem ein Timer regelmäßig auf dieses nil prüft und dann das Programm wieder vor holt.
  • Erst
    Delphi-Quellcode:
    Application.BringToFront;
    und dann wird in Screen.CustomForms das erste "sichtbare" Fenster gesucht (ja, im gegensatz zu Emba hatte ich das damals schon geprüft), da ebenfalls BringToFront und wenn IsWindowEnabled auch noch SetFocus. (windosseitig sind Forms disabled, wenn z.B. ein modaler Dialog davor liegt)
    Dann ist vielleicht eine "falsche" Form aktiv, aber die Kunden beschwerten sich wenigstens nicht mehr, dass das Programm komplett (im Hintergrund) verschwindet. (die sind es inzwischen eh gewohnt, daß der Fokus nicht immer dahin geht, wo er zuletzt war)

Sir Rufo 23. Apr 2014 11:48

AW: Fokusproblem (MDI)
 
Statt dem Timer wäre auch
Delphi-Quellcode:
TApplicationEvents.OnIdle
möglich (find ich in dem Zusammenhang sogar besser)

himitsu 23. Apr 2014 12:11

AW: Diverse Fokusprobleme
 
OK, OnIdle wäre vielleicht auch gegangen.

Es wurde aber absichtlich ein Timer, damit ich ein zeitlich einheitliches und nachvollziehbares Verhalten hinbekomm.
Welcher aktuell mit einem 100ms-Intervall nach je 10 Durchläufen den Status prüft, also alle 1 Sekunde.

Wenn das Programm hängt (z.B. irgendwas Laden oder eine SQL-Abfrage), dann verlängert sich das Intervall entsprechend und löst nicht sofort nach dem Hängen aus.
Das verhinderte recht zuverlässig, daß der Code auch auslöst, wenn dieses wirklich mal kurzzeitig nil ist (z.B. beim Wechsel von Fenstern), aber kurz danach von "alleine" wieder in einen korrekten Zustand gerät, womit mein Reparaturcode sonst den "korrekten" Fokus überschreiben würde.

Und im Grunde war es nur ein "kurzfristiger" Bugfix (seit vielleicht 2 Jahren), bis die Fehler behoben sind. :stupid:

Gerd01 30. Apr 2014 09:58

AW: Diverse Fokusprobleme
 
Ich kenne das Problem auch schon lange bei TFindDialog. Zum Beispiel in anderen Forms als in der Mainform. Ich habe dafür folgenden Workaround in Application on Message:

Code:
procedure TfmGeMail.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
var
  Shift: TShiftState;
  Key: Word;

  procedure GetShiftState(var Shift: TShiftState);
  var
    KeyboardState: TKeyboardState;
  begin
    Shift := [];
    GetKeyboardState(KeyboardState);
    if (KeyboardState[vk_Shift] and 128 = 128) then
      Shift := Shift + [ssShift];
    if (KeyboardState[VK_CONTROL] and 128 = 128) then
      Shift := Shift + [ssCtrl];
    if (KeyboardState[vk_Menu] and 128 = 128) then
      Shift := Shift + [ssAlt];
  end;


begin
  GetShiftState(Shift);
  Key := Msg.WParam;
  case Msg.Message of
    WM_KEYDOWN:
      case Key of
        Ord('F'): if (Shift=[ssCtrl]) then
                  begin
                    SetWindowPos (Application.handle,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE+SWP_NOSIZE or SWP_NOREDRAW);
                    SetWindowPos(Application.Handle, HWND_NOTOPMOST, 0,0,0,0, SWP_NOMOVE or SWP_NOSIZE or SWP_NOREDRAW );
                  end;


...
Dann werden die Fenster wieder in der richtigen Reihenfolge und der FindDialog verschwindet nicht und ist focussiert

himitsu 30. Apr 2014 14:42

AW: Diverse Fokusprobleme
 
Den TopMost-Schrott haben wir das letzte halbe/ganze Jahr über wieder komplett ausgebaut, da es mehr Probleme machte, als es löste.
Vorallem da dann ständig immer wieder mit Application.NormalizeTopMosts und Co. rumgepfuscht werden muß, wenn man einen Dialog/Fehlermeldung anzeigen will, damit die nicht hinter TopMost-Fenstern verschwinden.

Eigentlich lässt sich das inzwischen besser über den PopupMode regeln (wenn es denn auch überall korrekt verwendet würde).


Und bei Application.Handle musst du auch aufpassen, denn das "versteckte" Application-Message-Window wird von Delphis nicht mehr für die Anzeige verwendet, seitdem Delphi sich kompatibler zu Vista+ verhällt.
Den in alten Delphis funktioniert z.B. das Aero-Preview nicht, da man in der Taskleiste nur das "leere" Application-Fenster sieht und nicht die MainForm.
siehe Application.MainFormOnTaskBar

Sir Rufo 3. Mai 2014 10:35

AW: Diverse Fokusprobleme
 
Gerade bei SO gefunden

Hast du schon einen QC Eintrag verfasst?


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:49 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz