Laden...

Event-Behandlung zwischen 2 Forms verschieben/austauschen

Erstellt von trib vor 11 Jahren Letzter Beitrag vor 11 Jahren 4.675 Views
T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren
Event-Behandlung zwischen 2 Forms verschieben/austauschen

Hallo zusammen,

in einem Projekt für Windows CE muss ich einen RFID-Scanner per DLL ansprechen und nicht wie gewohnt als Tastatureingabe interpretieren.

Nun habe ich quasi 2 Eingabearten:
Einmal die klassische Textbox & ein modales Eingabefenster welches aus dem Code heraus geöffnet werden kann.

Der Ablauf ist aktuell so:

private void Form1_Load()
{
     RFIDScanner.Scanned += new EventHandler<RFIDScanner.RfidScanEventArgs>(MainForm_RfidScanned);
}

private void Form1_Closing(object sender, CancelEventArgs e)
{
     RFIDScanner.Scanned -= new EventHandler<RFIDScanner.RfidScanEventArgs>(MainForm_RfidScanned);
}

private void Aktion()
{
    if(somethingSpecial)
    {
          RFIDScanner.Scanned -= new EventHandler<RFIDScanner.RfidScanEventArgs>(MainForm_RfidScanned);
          MyInputDlg dlg = new MyInputDlg("Bitte Menge hinterlegen");
          dlg.ShowDialog();
          ProcessInput(dlg.ConvertedOutput);
          RFIDScanner.Scanned += new EventHandler<RFIDScanner.RfidScanEventArgs>(MainForm_RfidScanned);
    }
}

Die MyInputDlg() Klasse beinhaltet dieselbe Logik das Event zu abbonieren und die Eingabe auszuwerten. (Im Init Abbonieren, beim schließen die Registrierung aufheben)

Nun ist das Verhalten folgendes:
Die Form startet und ich kann meine Textfelder per RFID füllen.
Die Businesslogik entscheidet, dass noch eine weitere Eingabe zwingend notwenig ist, entfernt das Event und startet modal meine Eingabeform. (Mal eben plakativ "Aktion()" genannt)
Diese Abboniert das Event wieder. Allerdings passiert nichts bis ich das Fenster manuell fülle und schließe. Dann registriert sich wieder die Hauptform und bekommt das Event für das Eingabefenster ab.

Warum? 😃
Starte ich diesen InputDialog im INIT der Form, bevor die Form selbst das Event registriert, funktioniert es hervorragend.

16.835 Beiträge seit 2008
vor 11 Jahren

Habs nur grob überflogen, aber Dein ShowDialog blockiert an dieser Stelle meines Wissens.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo trib,

mal abgesehen davon, dass ich persönlich sowieso kein Freund davon bin, EventHandler zwischenzeitliche zu deregistrieren, sondern es vorziehe, über eine boolsche Variable zu steuern, was der aufgerufene EventHandler tun soll bzw. ob er überhaupt was tun soll, siehe [FAQ] Event nur bei Benutzeraktion auslösen, nicht bei programmtechnischer Änderung, sollte es schon so funktionieren. Es wäre eigentlich auch unwahrscheinlich, dass der Scanner schaut, welcher EventHandler bei ihm zuerst registriert wurde und die Events anschließend nur an diesen EventHander ausliefert, obwohl das technisch machbar wäre.

Lass doch einfach den bzw. beide EventHandler registriert. Werden die dann (beide) aufgerufen?

herbivore

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren

Hallo Abt,

Habs nur grob überflogen, aber Dein ShowDialog blockiert an dieser Stelle meines Wissens.

Das scheint mir leider genauso. Allerdings gibt es eine Form, die beim Initialisieren eine Nummer MyInputDlg() abfragt. Modal natürlich und es funktioniert. Danach erfolgt die normale Eingabe in Textfelder. Wenn ich jetzt wieder mein MyInputDlg() starte funktioniert es nicht mehr 😕

Ohne Dialog kann ich den Benutzer schlecht "zwingen" eine Eingabe vorzunehmen. Vorallem, da dort z.B. nur Zahlen eingegeben werden dürfen und sich der Dialog sonst nicht schließen lässt.

Hallo herbivore,

mal abgesehen davon, dass ich persönlich sowieso kein Freund davon bin, EventHandler zwischenzeitliche zu deregistrieren, sondern es vorziehe, über eine boolsche Variable zu steuern, was der aufgerufene EventHandler tun soll bzw. ob er überhaupt was tun soll, siehe
>
, sollte es schon so funktionieren.

Das klingt schon deutlich stimmiger! Das Event wird nun nicht deregistriert, sondern per Variable die Verarbeitung übersprungen.
Ergebnis ist leider nahezu unverändert. Der Dialog steht & nix passiert. Fenster wird geschlossen und das Event aufgerufen. Dank der Variablen wir die Eingabe in der Hauptform ignoriert und es geht unverändert weiter im Code.

Es wäre eigentlich auch unwahrscheinlich, dass der Scanner schaut, welcher EventHandler bei ihm zuerst registriert wurde und die Events anschließend nur an diesen EventHander ausliefert, obwohl das technisch machbar wäre.

Lass doch einfach den bzw. beide EventHandler registriert. Werden die dann (beide) aufgerufen?

Das Event des Scanners habe ich zentral abonniert und stelle ein eigenes Event mit den nötigen Rückgabeinformationen bereit. Da habe ich definitiv keine Priorisierung integriert.
Auch dieses zentrale Event wird erst aufgerufen, wenn der Dialog bestätigt wurde.

Es wird immer nur das Event in der Hauptform aufgerufen. Das aber auch erst nach Ende des ShowDialog´s.
Kann ja schlecht das Fenster in einem anderen Thread Modal aufrufen. Oder ist das etwa die dreckige Lösung?

U
1.688 Beiträge seit 2007
vor 11 Jahren

Hallo,

wo und wie erzeugst Du die Klasse RFIDScanner (bzw. in dieser die Hardwareanbindung)? Was ist RFIDScanner?

Kann ja schlecht das Fenster in einem anderen Thread Modal aufrufen. Oder ist das etwa die dreckige Lösung?

Ich würde mal versuchen, RFIDScanner (wenn es unabhängig von der GUI ist) in einem separaten Thread zu erzeugen, diesen auch bis zum Programmende nicht zu beenden (ManualResetEvent), und dann alle Zugriffe aus den Eventhandlern wie in [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) benutzen.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo trib,

Es wird immer nur das Event in der Hauptform aufgerufen. Das aber auch erst nach Ende des ShowDialog´s.

sehr merkwürdig. Eigentlich führt ShowDialog die Nachrichtenschleife während der Anzeige des Dialogs weiter. Es sollten also alle Nachrichten ganz normal verarbeitet werden und damit auch die Scanner-Events. Aber irgendwas wird wohl doch anders sein.

Kann ja schlecht das Fenster in einem anderen Thread Modal aufrufen. Oder ist das etwa die dreckige Lösung?

Das wird in der Tat schwierig, weil modale Fenster sich kennen und direkt aufeinander zugreifen (ohne [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke) zu berücksichtigen). Da wird es schnell Peng machen. Davon kann ich also nur abraten.

Abraten würde ich allerdings sowieso von modalen Dialogen, siehe Warten auf Schließen einer anderen Form [und warum man Dialoge nicht modal machen sollte]. Mach es doch einfach so, wie dort beschrieben. Öffne das (Dialog-)Fenster per Show und pack alles, was hinter dem jetzigen ShowDialog steht, in Form.FormClosing. Wenn du nicht willst, dass das Hauptfenster oder einige Controls daraus inhaltlich bedienbar ist, dann verwende dafür Enabled = false. Dann ist das Hauptfenster nicht nur weiterhin verschiebbar, sondern das könnte auch überhaupt dein Problem lösen.

herbivore

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren

wo und wie erzeugst Du die Klasse RFIDScanner (bzw. in dieser die Hardwareanbindung)? Was ist RFIDScanner?

Hi ujr,

das ist eine Singleton Klasse, die eine DLL von Motorola instanziert, die Einrichtung übergibt und das Event abonniert.
(Wie nun diese DLL die Hardware genau ansteuert, steht in den Sternen.)
Darin habe ich dann ein eigenes Event erstellt, da die EventArgs aus der DLL immer eine Referenzierung auf diese Motorola-DLL notwendig gemacht haben. So reiche ich einfach den Text durch, sofern der Lesevorgang erfolgreich war.

Bei Beendigung des Programmes schieße ich die Singleton-Klasse per Dispose ab, wo ich noch den Scanner deaktiviere und ein paar Ressourcen aufräume (Sonst gibt es viel geblinke am Scanner).

sehr merkwürdig. Eigentlich führt ShowDialog die Nachrichtenschleife während der Anzeige des Dialogs weiter. Es sollten also alle Nachrichten ganz normal verarbeitet werden und damit auch die Scanner-Events. Aber irgendwas wird wohl doch anders sein.

Darauf habe ich mich auch verlassen. Es funktioniert ja auch, sofern ich (noch)nicht in der Hauptform das Event registriert habe.

Das Modale Dialoge verhindert werden sollen habe ich schon oft gelesen und kann das gerade bei MDE-Scannern gut nachvollziehen. In diesem Fall arbeite ich aber Zeilenbasiert in einem DataGrid. Das bedeutet, ich muss pro Zeile reagieren und nicht erst am Schluss. Sonst ist der Arbeiter schon am Ende des Lagers und soll nochmal die Charge des 1. Artikels holen...

Dann bliebe noch die letzte Variante mit this.Enabled = false; Da sehe ich nur die Gefahr, dass der gemeine Lagermitarbeiter das Eingabefenster irgendwie in den Hintergrund "getoucht" bekommt. Das werde ich aber jetzt erstmal testen.

Vielen Dank schonmal!

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo trib,

Da sehe ich nur die Gefahr, dass der gemeine Lagermitarbeiter das Eingabefenster irgendwie in den Hintergrund "getoucht" bekommt.

um das zu verhindern, gibt es ja Form.Owner.

herbivore

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren

Hallo zusammen,

bisher habe ich noch keine adäquate Lösung gefunden um die bestehende Programmlogik größtenteils weiter nutzen zu können. (Alle Programmteile benötigen noch das Modale-Eingabefenster um den Ablauf kurzzeitig zu unterbrechen.)
Der Versuch die Singleton-Klasse mit dem Scanner Event in einem anderen Thread zu starten ist darin geendet, dass der Garbage-Collector diese nach exakt einer Ausführung gekillt hat.

Mein aktueller Workaround sieht nun so aus, dass ich den Scanner nicht mehr zentral aktiviere und überall dort wo es nötig ist das Event abonniere, sondern nur pro Eingabe.
Das bedeutet, ich rufe
RFIDScanner.Instance.Dispose() auf, öffne mein MyInputDlg() und starte danach wieder den Scanner mit RFIDScanner.Instance.Init().
Wobei MyInputDlg() den Init und das Dispose ebenfalls ausführt. Blöd den Scanner jetzt nicht dauerhaft im Hintergrund aktiv zu halten, aber irgendwie scheint das Initialisieren der Singleton-Klasse, das Event trotzdem nur an die Haupform zu binden und mit dem ShowDialog zu unterbrechen.

Danke für eure Unterstützung! Werde am Ball bleiben und nach einer "schöneren" Lösung suchen.

U
1.688 Beiträge seit 2007
vor 11 Jahren

Hallo,

wodurch wird eigentlich "Aktion()" aufgerufen? Wie sieht der Call-Stack aus?

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren

wodurch wird eigentlich "Aktion()" aufgerufen? Wie sieht der Call-Stack aus?

Hi urj,
habe heute leider keinen MDE hier. Also manuell beschrieben:

Es gibt eine Hauptform mit ein paar Buttons. Diese starten dann z.B. die Form für Lagerentnahmen.
Im Init dieser Form muss ich dann schon wissen vor welchem Regal der Mitarbeiter steht und hole mir per Dialog einen Wert vom Scanner.*Daten füllen, DataGrid hübsch machen, usw. *Nun gibt es eine Textbox auf der Form die wieder vom Scanner gefüllt wird. (ArtikelNr.)
Der Mitarbeiter scannt einen Artikel Code, die Textbox gefüllt (txtInput_KeyDown aufgerufen) und nun wird entschieden ob der Artikel eine Chargen Nummer benötigt oder nicht (per Webservice).

*Ja: Action() wird aufgerufen (Dialog geht auf, Scanner aktiviert) *Nein: Nix passiert, Charge bleibt leer *Artikel wird per Webservice entnommen, DataGrid aktualisiert, Focus auf die Textbox.

Das sind prinzipiell die Abläufe. Jetzt wo ich es so schreibe wird klar, dass ich ja aus dem Event des Scanners wieder den Scanner aufrufe... Das könnte natürlich der Knackpunkt sein oO

U
1.688 Beiträge seit 2007
vor 11 Jahren

dass ich ja aus dem Event des Scanners wieder den Scanner aufrufe... Das könnte natürlich der Knackpunkt sein oO

Darauf wollte ich hinaus 😁
Darum funktioniert's auch beim Start des Programms, da dort keine analoge Kette aufgebaut wurde.

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren

Ist aber trotzdem etwas verrückt, da ich zum einen das Event vom Scanner bekomme und dann dadurch das Event vom KeyDown auslöse.
Damit hätte ich gedacht wieder im Main-Thread zu sein. Sonst hätte es ja auch zwingend ein Invoke benötigt um mein DataGrid zu aktualisieren.

Dann wird es aber auch keinen besseren Workaround geben, als den Scanner immer zu aktivieren und wieder zu deaktivieren, da ich das sonst nie synchron bekomme 😕

U
1.688 Beiträge seit 2007
vor 11 Jahren

Ist aber trotzdem etwas verrückt, da ich zum einen das Event vom Scanner bekomme und dann dadurch das Event vom KeyDown auslöse.
Damit hätte ich gedacht wieder im Main-Thread zu sein. Sonst hätte es ja auch zwingend ein Invoke benötigt um mein DataGrid zu aktualisieren./

Vermutlich bist Du immer im Main-Thread. Wie löst Du denn KeyDown aus? Durch Aufruf? Das führt zu keinem Thread-Kontext-Wechsel.

Dann wird es aber auch keinen besseren Workaround geben, als den Scanner immer zu aktivieren und wieder zu deaktivieren, da ich das sonst nie synchron bekomme 😕

Dann deaktivierst Du den Scanner aus seiner Ereignisbehandlung heraus? Auch nicht schick.

Du könntest mal versuchen, bspw. KeyDown über MainForm.BeginInvoke aufzurufen - und wenn's nur zum Test ist.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo ujr,

Du könntest mal versuchen, bspw. KeyDown über MainForm.BeginInvoke aufzurufen - und wenn's nur zum Test ist.

trib könnte mal versuchen, Action per Control.BeginInvoke aufzurufen - und wenn's nur zum Test ist. Das scheint mir sinniger.

herbivore

T
trib Themenstarter:in
708 Beiträge seit 2008
vor 11 Jahren

Wie löst Du denn KeyDown aus?

Ganz klassisch per txtInput.Text = MyInputDlg() damit der User aus sieht was dort eingescannt wurde.
Im KeyDown wird dann auf den e.KeyCode == Return geprüft.

Dann deaktivierst Du den Scanner aus seiner Ereignisbehandlung heraus? Auch nicht schick.

Du könntest mal versuchen, bspw. KeyDown über MainForm.BeginInvoke aufzurufen - und wenn's nur zum Test ist.

Ja, ich weiß. Und dann Initialisiere ich das Ganze jedes Mal wieder. Auch totaler Mist!
Deshalb hatte ich ja die Singelton Methode gewählt und mir erhofft, dass ich so mit dem Programmstart die externe DLL instanziere, den RFID-Leser aktiviere, das Event abgreife und überall abonniere wo es nötig ist.

Das ist eine gute Idee, das werde ich mal ausprobieren wenn ich den MDE morgen wieder in der Hand hab. In dem Fall dass ich da in einem anderen Thread bin, hätte ich aber schon erwartet, dass das Programm beim zuweisen von txtInput.Text eine Exception schmeißt...

@herbivore: So werde ich es dann auch machen und mich langsam bis zu dem KeyDown vorarbeiten.

Vielen Dank für Eure Ideen!