Ich möchte eine Basisklasse für (Dialoge) Forms erstellen, die Quasi-Modal angezeigt werden. Die Schaltflächen für das Schließen (DialogResult) liegen auf dem Aufrufer. Dieser sollte aber für alle anderen Aktivitäten gesperrt sein.
Der Anwendungsentwickler soll wie gewohnt schreiben können (o.ä.)
if (SeinDialog.ShowQuasiDialog() == DialogResult.OK)
{
...
}
Nun meine Probleme:
Mit Threads habe ich auch schon experimentiert, aber irgendwie klappt das nicht.
Hat jemend eine Idee?
Gruß Turmoil
Hallo Turmoil,
sei nicht böse, aber ich finde das Konzept ziemlich ungünstig. Ich denke die Lösung wird sein, das Konzept zu ändern, nicht eine Antwort auf die genannten Fragen zu finden.
Die Schaltflächen für das Schließen (DialogResult) liegen auf dem Aufrufer.
Warum soll das so sein?
herbivore
Also du willst quasi eine Art Child Window verstehe ich das richtig.
Das Fenster soll übergeblendet sein aber nicht modal.
Probier mal das Fenster als Child zu setzen. ( SetParent mit der WinApi z.B.)
Ansonsten gebe ich Hörbi aber recht, so sollte man's nicht machen.
Hallo Sebastian.Lange,
Das Fenster soll übergeblendet sein aber nicht modal.
doch das Fenster soll (im Wesentlichen) schon modal sen ... mit Ausnahme der Schaltflächen für das Schließen.
Probier mal das Fenster als Child zu setzen.
Dafür braucht man nicht Win32. Das geht auch in .NET direkt: Form.Owner.
Ansonsten gebe ich Hörbi aber recht, so sollte man's nicht machen.
eben 😃 Deshalb nochmal meine Frage an Turmoil: Warum soll das so sein?
herbivore
Hallo zusammen,
unsere Anwendung(en) laufen auf NC-gesteuerten Maschinen ohne Mausbedienung. Siemens ist da das Maß, an dem wir gemessen werden.
Das Schließen von Forms Dialogen etc. und die gesamte Programmsteuerung soll über Softkeys bwz. F-Tasten geschehen (siehe Bild im Anhang). Deshalb versuche ich mich gerade an eineme Basis-Dialog mit Knöpgen zum Schließen auf dem Hauptform, welches auch die anderen Ereignisse weiterleitet.
Gruß Turmoil
Dann lag ich ja doch nicht so falsch.. und irgendwie doch.
Du könntest evtl. das KeyEvent das modalen Dialogs ableiten
und dann das Event samt Taste und sich selbst als Parameter an eine Default Verabeitung deiner Applikation schicken.
Natürlich musst Du noch einiges mehr tun, die Dialoge verarbeiten, speichern oder tun ja sonst irgendwas. Verdonnere z.b. deine Dialoge via Interface dazu eine Apply und eine Cancel Methode zu implementieren, diese könnte die Default verarbeitung aufrufen wenn sie meint die taste stimmt.
Naja halt irgendwie so..
Gruss!
Sebastian
Das KeyEvent ist nicht das Problem. Dies habe ich mittels Frame-/ClientForm Beziehung schon gelöst. Events werden dabei mittels KeyPress oder mittels Button-Click ausgelöst.
Mein Problem ist das Drücken bzw. Enabled setzen der Knöpfe auf dem Frame- bzw. OwnerForm des Dialogs.
Das Anzeigen des Dialogs sollte sich wie eine statische Klassenfunktion verhalten (öffnen und auf schließen warten vgl. MessageBox.Show() oder Form.ShowDiaog()).
Gruß Turmoil
Was ist eine NC-gesteuerten Maschine?
Ansonsten vllt Menüs greifen, und jedem Menu-Item einen Accessor-Key verpassen.
Außerdem ShortCuts.
Die ShortCut-Accessoren auch im MenuItem anzeigen (ShortCutKeyDisplayString).
Jedenfalls nicht das Gui vollballern mit Knöppen, auf die man nicht klicksen kann.
Das Menu-Konzept bietet halt die Möglichkeit, beliebig viele Knöppe unterzubringen, ohne beliebig viel Platz wegzunehmen.
Außerdem ist alles beschriftet und sinnvoll strukturiert.
ShortCuts lernen sich mit der Zeit dann von selbst.
Der frühe Apfel fängt den Wurm.
Was ist eine NC-gesteuerten Maschine?
siehe:
Wikipedia -CNC
Siemens-Pressemitteilungen
Die Knöpfe auf dem Frameform der Anwendung sind sozusagen die immer angezeigten Menüs (siehe Bild weiter oben). Es werden dann je nach Clientform Events inklusive Beschriftung an-/abgemeldet.
Ich würde nun gerne Dialogfunktionalität dort hinterlassen.
Gruß Turmoil
Hallo Turmoil,
wenn die Knöpfe übergeordnet sind, warum packst du sie dann nicht in ein extra Fenster? Dann machst du den Dialog modal zum Hauptfenster und kannst, weil die Knöpfe in einem unabhängigen Fenster sind, diese weiter bedienen.
herbivore
Hallo herbivore,
wenn ich das From mit ShowDialog() öffne, dann läßt sich die ganze Applikation nicht mehr bedienen. Egal welches Form ich zuvor anzeige.
Oder wie war das gemeint?
Dann machst du den Dialog modal zum Hauptfenster
Gruß Turmoil
Ich dachte, das sei der gewünschte Effekt, so habich jedenfalls dein ersten Post verstanden.
Die Schaltflächen für das Schließen (DialogResult) liegen auf dem Aufrufer. Dieser sollte aber für alle anderen Aktivitäten gesperrt sein.
Ich würde bei Buttons, die mangels Maus nicht klickbar sind, auch nicht von Buttons reden (oder solche nehmen) sondern das sind wohl eher Labels.
Und Steuerung ist eben Tastatur.
Wie gesagt, ein Menu ist auch Tastatur-gesteuert, und viel logischer aufgebaut, und setzt sich als Steuerung auch klar ab von üblichen Tastatur-Eingaben (aber vllt. gibts da ja auch keine).
Aber krass unlogisch und konträr zu allen Windows-Anwendungen, die ich kenne, ist die Idee, den Schließen-Button eines Forms ausserhalb desselben zu positionieren.
Sonen Button (oder ein dahingehend beschriftetes Label) tätich so verstehen, dasses zum Schließen der Hauptanwendung führt.
Der frühe Apfel fängt den Wurm.
- Wenn ich es Eventgesteuert zu lösen versuche, dann geht die Bedienerfreundlichkeit (für den Anwendungsentwickler) verloren. Es müsssen dann mind. 2 Stellen richtig überschrieben werden.
Das wäre aber ihmo der richtige Weg. Schliesslich hast du ja auch andere Vorraussetzungen als bei der normalen Dialogbox.
Aber ne Möglichkeit wäre vllt, die entsprechenden Methoden öffentlich zu machen und mit den Buttons dann darauf zu verweisen.
btnOk.Click += dialogBox.BtnOk_Clicked(...);
Hallo Turmoil,
wenn ich das From mit ShowDialog() öffne, dann läßt sich die ganze Applikation nicht mehr bedienen.
das stimmt nicht ganz. Es lässt sich kein Fenster mehr bedienen, dass in dem gleichen Thread läuft wie der Dialog. Nun bin ich zwar im Allgemeinen ein Verfechter von: "eine Anwendung soll nur einen GUI-Thread haben". Aber besondere Situationen erfordern besondere Maßnahmen. Wenn du also das extra Fenster in einem anderen Thread laufen lässt (der dann natürlich sein eigenes Application.Run haben muss), gehts so wie ich vorgeschlagen habe.
herbivore
Hallo Zusmmen.
@ErfinderDesRades:
Bei unseren Anwendungen handelt es sich nicht um klassische Windows-Forms-Applikationen. Ich habe Steuer-Knöpfe im Hauptform platziert, die mit der Maus gedückt werden können (ist auf unseren NC-Maschinen aber nicht die Regel, eher am PC bei der Entwicklung), deren ClickEvent aber ebenso über F-Tasten ausgelöst werden (idR. wird dies so auf den Maschinen passieren).
@Alf Ator:
Dies (btnOk.Click += Event(...)) habe ich so ähnlich schon realisiert. Ob eventgesteuert der richtige Weg ist, kann ich so nicht sagen. Mein Chef und die Anwendungsentwickler hätten es bei einem modalen Dialog einfacher.
@herbivore:
Ich werde es mal mit einem extra Thread und Application.Run versuchen. Gibts da irgendwo ein Bsp.? Ich habe schon etwas gestöbert, aber noch nicht den richtigen Beitrag gefunden.
Gruß Turmoil
Hallo Turmoil,
was brauchst du da für ein Beispiel? Thread starten und Application.Run mit new MyForm als Parameter aufrufen. Mehr ist es doch nicht.
herbivore
Hallo herbivore.
tmpThread = new Thread(new ThreadStart(F.Show));
tmpThread.Start();
Application.Run(F);
Löst bei Application.Run(F) die nachfolgende Fehlermeldung
Starting a second message loop on a single thread is not a valid operation. Use Form.ShowDialog instead.
und ohne Application.Run geht das Form sofort wieder zu.
Hab mit Threading noch wenig Erfahrung, sorry. Kleiner Tipp ?(
Gruß Turmoil
Hallo Turmoil,
... in einem anderen Thread laufen lässt (der dann natürlich sein eigenes Application.Run haben muss) ...
herbivore
Hallo Zusmmen.
@ErfinderDesRades:
Bei unseren Anwendungen handelt es sich nicht um klassische Windows-Forms-Applikationen. Ich habe Steuer-Knöpfe im Hauptform platziert, die mit der Maus gedückt werden können (ist auf unseren NC-Maschinen aber nicht die Regel, eher am PC bei der Entwicklung)
Ui, da musste aber aufpassen!
Sonst bastelstene prima Anwendung, die auf der Maschine dann nicht bedienbar ist.
Evtl jetzt erst recht keine Buttons nehmen.
Der frühe Apfel fängt den Wurm.
Hallo herbivore.
ich habe einiges versucht aber dennoch funktioniert es nicht so wie erwartet (das Einkommentieren von Application.Run() hat m.E. keine Auswirkung).
Wenn ich die Knöpfe des FrameForms nicht ändere, dann geht das DialogForm auf und nach dem Schließen gehts auch weiter. Wenn ich aber mittels ThreadEventsAnmelden die Events des Dialogforms beim FrameForm anmelde, hängt sich mein GUI auf (wahrscheinlich häng mein GUI (FrameForm) auch ohne Anmelden der Dialog-Events).
Ich habe schon den Beitrag in den FAQ gelesen, leider widersprechen sich da einige Aussagen bzw. passen nicht auf den hier vorliegenden Fall.
Hier mein vereinfachter Quellcode:
class BFrameForm: Form
{
.....
private Type DialogFormType;
private DialogResult DialogRes;
private delegate void EventsAnmelden(List<ButtonEvent> EList); // Liste mit Delegaten für die Knöpfe
private EventsAnmelden ThreadEventsAnmelden;
private void DialogFormShowThread()
{
if (DialogFormType != null)
{
Form F = (Form)Activator.CreateInstance(DialogFormType);
if (F is BDialogForm)
{
this.Invoke(this.ThreadEventsAnmelden, new Object[] {(F as BDialogForm).EventList });
}
F.TopMost = true;
//Application.Run(F);
DialogRes = F.ShowDialog();
}
}
public DialogResult DialogFormShow(Type FormType)
{
if (FormType != null)
{
DialogFormType = FormType;
PnlBackground.Enabled = false;
LoggingOffEvents(); // eigene Events von den Knöpfen abmelden
this.ThreadEventsAnmelden = new EventsAnmelden(this.LoggingOnEvents);
// neuen Thread mit Methode die den Dialog anzeigt starten
Thread tmpThread = new Thread(new ThreadStart(DialogFormShowThread));
tmpThread.Start();
while (tmpThread.IsAlive)
{
Thread.Sleep(100);
}
this.OwnEventsAnmelden = null;
PnlBackground.Enabled = true;
LoggingOffEvents(); // Events von den Knöpfen abmelden
LoggingOnEvents(OwnEventList); // eigene Events von den Knöpfen anmelden
return DialogRes;
}
else
{ return DialogResult.Cancel; }
}
....
}
Wo liegt denn da der Hase begraben?
Gruß Turmoil
Hallo Turmoil,
sorry, ich verstehe nicht, was du da treibst. Starte einen Thread. Erzeuge darin das Form mit den Knöpfen. Rufe mit diesem Form als Parameter Application.Run auf. Das sind drei Zeilen Code. Mehr ist es nicht. Alles andere machst du wie vorher.
Das einzige wo du aufpassen musst, ist das Events, die von den Knöpfen gefeuert werden, jetzt in dem neu erzeugen Thread laufen. Wenn du aus den EventHandlern auf Forms im eigentlichen GUI-Thread aufrufen willst, musst du also ganz normal [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke) beachten.
Ich habe schon den Beitrag in den FAQ gelesen, leider widersprechen sich da einige Aussagen
Glaube ich erstmal nicht. Welche?
herbivore
Hallo herbivore,
Widersprüche:
Das Form mit den Knöpfen ist das Hauptform der Applikation. Ich habe den Programmstart s.u. umgestellt, aber die Knöpfe funktionieren trotzdem nicht. Das sieht aber irgendwie komisch aus, oder
static class Program
{
/// <summary> Der Haupteinstiegspunkt für die Anwendung. </summary>
static void Main()
{
Thread MainThread = null;
if (MainThread != null)
{
MainThread = new Thread(new ThreadStart(Main));
MainThread.Start();
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
TestFrameForm f = new TestFrameForm();
f.FormInit();
Application.Run(f);
f.FormDone();
}
}
Ich glaub ich lass es. Ich werde es Eventgesteuert lösen.
Danke an Alle für die Bemühungen.
Gruß Turmoil
Hallo Turmoil,
ich habe ja schon oben gesagt:
Nun bin ich zwar im Allgemeinen ein Verfechter von: "eine Anwendung soll nur einen GUI-Thread haben". Aber besondere Situationen erfordern besondere Maßnahmen.
Insofern besteht kein Widerspruch, sondern es liegt hier nur eine Ausnahme vor.
Außerdem steht in der FAQ nicht, dass kurze Anweisungen in den zweiten Thread ausgelagert werden sollen, sondern im Gegenteil steht das da für langlaufende Aktionen.
Es ist auch kein Widerspruch, dass es nur einen GUI-Thread geben, aber trotzdem einen zweiten Worker-Thread für langlaufende Aktionen geben soll. Denn GUI-Thread ist gerade ungleich Worker-Thread.
Das Form mit den Knöpfen ist das Hauptform der Applikation.
Und mein Vorschlag war, die Knöpfe in ein extra Form (also ein drittes Form) auszulagern. Wenn die Knöpfe weiterhin im Hauptform liegen funktioniert der Vorschlag mit dem zweiten Thread natürlich nicht.
herbivore
Application.Run in einen neuen Thread
new Thread(new ThreadStart(delegate()
{
Form form = new Form();
Application.Run(form);
})).Start();
Die Eingaben würde ich nicht über die Forms nehmen sondern über einen Hook. Diese werden dan an eine Klasse weitergereicht die diese verarbeitet.
Oder über Linq:
new Thread(() =>
{
Form form = new Form();
Application.Run(form);
}).Start();