Dialoge: Instanziierung von Forms und Aufruf von Dialogen

    • VB.NET

    Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

      Dialoge: Instanziierung von Forms und Aufruf von Dialogen

      Desöfteren kommt mir hier solch VB6-Kompatibilitäts-Zeugs entgegen:

      VB.NET-Quellcode

      1. Form2.Show()
      2. Form2.Show(Me)
      3. Form2.ShowDialog()
      4. Form2.ShowDialog(Me)
      Dies wird aufgerufen, weil es irgendwo steht (ich frage mich nur wo), weil es die jungen Kollegen (leider) falsch gezeigt bekommen oder weil es einfach, einfach VB6, ist.
      Ich werde hier nicht auf die Unterschiede zwischen den 4 Aufrufen eingehen, nur soviel: Show(x) ruft einen nicht modalen Dialog, ShowDialog(x) ruft einen modalen Dialog auf.
      Diese Aufrufe sind möglich, weil VB.NET eine implizite Instanz des Dialogs erstellt und anzeigt, beim Verlassen der aufrufenden Prozedur wird sie wieder zerstört. Für VB6 mag das reichen, für .NET sollte es nicht mehr reichen.
      2 einfache Beispiele, wo die VB6-Funktionalität nicht ausreicht, wo nur .NET helfen kann:

      1. Wir wollen vom 2. Dialog aus ein Event zur MainForm senden.
      Event-Code in Form2:

      VB.NET-Quellcode

      1. Public Event MyEvent(sender As System.Object, e As System.EventArgs)
      2. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      3. RaiseEvent MyEvent(Me, EventArgs.Empty)
      4. End Sub
      Wenn wir in Form1 dür dieses Event einen Handler anlegen wollen, bekommen wir dies angeboten:

      Da kommt Form2 nicht vor, weil Form2 der Name der Klasse, nicht aber der Name der Instanz ist.
      Also müssen wir mit New eine Instanz der Form2 erstellen.
      Damit dies ordentlich funktioniert: nicht modaler Dialog, beliebig oft aufruf- und wieder schließbar, verwenden wir diesen Code:
      nicht modaler Dialog

      VB.NET-Quellcode

      1. Private WithEvents frm2 As Form2
      2. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      3. If frm2 Is Nothing OrElse frm2.IsDisposed Then
      4. frm2 = New Form2
      5. End If
      6. If Not frm2.Visible Then
      7. frm2.Show(Me)
      8. End If
      9. If frm2.WindowState = FormWindowState.Minimized Then
      10. frm2.WindowState = FormWindowState.Normal
      11. End If
      12. End Sub
      Den Eventhandler wie auf dem Bild:
      Eventhandler

      VB.NET-Quellcode

      1. Private Sub Form2_Click(sender As System.Object, e As System.EventArgs) Handles frm2.MyEvent
      2. MessageBox.Show("Event empfangen")
      3. End Sub
      Und nun los, Start, der Button öffnet den nicht modalen Dialog, und beim Drücken des Buttons in der 2. Form kommt die MessageBox im Hauptfenster.
      Dies funktioniert mit Form2.Show() nicht!

      2. Wir wollen gleichzeitig 2 Mal den Dialog aufrufen.
      Nichts leichter als das, mit dem
      VB6-Code

      VB.NET-Quellcode

      1. Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
      2. Form2.Show(Me)
      3. End Sub
      4. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
      5. Form2.Show(Me)
      6. End Sub
      passiert nach Klick auf Button2 beim Klick auf Button3 dies:
      ;(
      Also schreiben wir analog zu oben folgenden Code:
      nicht modaler Dialog 2

      VB.NET-Quellcode

      1. Private WithEvents frm3 As Form2
      2. Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
      3. If frm3 Is Nothing OrElse frm3.IsDisposed Then
      4. frm3 = New Form2
      5. End If
      6. If Not frm3.Visible Then
      7. frm3.Show(Me)
      8. End If
      9. If frm3.WindowState = FormWindowState.Minimized Then
      10. frm3.WindowState = FormWindowState.Normal
      11. End If
      12. End Sub
      Nach Anpassung des Eventhandlers
      neuer Eventhandlers

      VB.NET-Quellcode

      1. Private Sub Form2_Click(sender As System.Object, e As System.EventArgs) Handles frm2.MyEvent, frm3.MyEvent
      2. If sender Is frm2 Then
      3. MessageBox.Show("Event frm2 empfangen")
      4. ElseIf sender Is frm3 Then
      5. MessageBox.Show("Event frm3 empfangen")
      6. End If
      7. End Sub
      können wir also von einer Formklasse mehrere Instanzen erstellen und mit ihnen kommunizieren:



      Ich hoffe, nun können wir unter .NET das Ende des Un-Aufrufs Form2.Show() einläuten. :D
      Die Begründung: Unter C# geht Form2.Show(); nicht sollte uns ermahnen, es gleich anständig zu machen.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ () aus folgendem Grund: Minimized ==> Normal

      Nicht modale Dialoge:
      In diesem Thread wird eine Instanz bei einem Tastendruck erzeugt. Das gibt einen niedlichen Effekt, den ich mal beschreiben möchte.
      Zum besseren Versgtändnis in einer Button-Klick-Prozedur:

      VB.NET-Quellcode

      1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      2. Dim dlg As New Form2
      3. dlg.Show(Me)
      4. End Sub
      Bei jedem Klick wird nun eine Instanz des Dialoges erzeugt und angezeigt, bis das Hauptprogramm oder diese Instanz explizit geschlossen wird:

      Die Instanzen liegen in der Garbage Collection, man kann sie über

      VB.NET-Quellcode

      1. For Each frm As Form In My.Application.OpenForms
      2. frm.Text = "bla"
      3. Next
      erreichen. Wenn das jemand so machen will - ich nicht.
      Ich bin aber dafür, dass wir willen sollten, welche Instanzen wo gehalten werden, also habe ich oben die Instanz(en) in die Klasse gelegt.
      Allerdings muss dabei aufgepasst werden, ob die jeweilige Variable schon instanziiertr bzw. der Dialog schon wieder geschlossen wurde.
      So ist gesichert, dass bei Button-Klick diese eine Instanz stets wieder angezeigt werden kann.
      ----------------------------
      Modale Dialoge:
      Da ein modaler Dialog die Ausführung des aufrufenden Programms anhält, ist es nicht erforderlich, die Dialog-Variable in der Klasse zu deklarieren, das kann in der aufrufenden Prozedur erfolgen. Damit vereinfacht sich vieles.

      VB.NET-Quellcode

      1. Dim dlg As New Form2
      2. dlg.ShowDialog()
      Da die Klasse Form das IDisposable-Interface implementiert, bietet sich eine Using-Kapselung der Dialog-Instanz an:

      VB.NET-Quellcode

      1. Using dlg As New Form2
      2. dlg.ShowDialog()
      3. End Using
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      modale Dialoge richtig implementieren

      Unter einem Dialog versteht man ein Form mit einem Ok-Button, und einem Cancel-Button, welches modal aufgerufen wird, in der Art:

      VB.NET-Quellcode

      1. dim result As DialogResult = dlgForm.ShowDialog()
      User-Eingaben in dieses dlgForm werden nur dann verarbeitet, wenn nicht gecancelt wurde.

      Dieser Dialog-Pattern wird vielfach verwendet, ob nun in einem LogIn-Formular oder um mw. in einer Datenverarbeitung einen einzelnen Datensatz zu editieren.

      Winforms unterstützt diesen Pattern auch in gradezu vollkommener Weise - ich meine damit, man kann die ganze Funktionalität mit Canceln oder Bestätigen mit ein paar Klicks im Designer zurechtmachen, ohne iwelchen Button-Click-Ereigniscode schreiben zu müssen. Wichtig sind folgende im Designer zu tätigenden Einstellungen:

      Einstellungen im Designer schrieb:

      dlgForm.CancelButton = btCancel
      dlgForm.AcceptButton = btOk
      btOk.DialogResult = DialogResult.Ok
      btCancel.DialogResult = DialogResult.Cancel

      Dabei erhält man die Funktionalität auch auf den Tasten, also [Esc] schließt den Dialog mit DialogResult.Cancel, und [Enter] schließt ihn mit DialogResult.Ok.

      Ein Dialog steht nie alleine, sondern wird von einem übergeordneten Form aufgerufen, und seine Eingaben werden anschließend ausgewertet. Hier ein Prinzip-Beispiel für einen LogIn-Dialog:

      VB.NET-Quellcode

      1. Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      2. Using dlgLg = New dlgLogin
      3. 'default-Werte vorgeben
      4. dlgLg.txtUser.Text = "User1"
      5. dlgLg.txtPassword.Text = "<bitte eingeben>"
      6. If dlgLg.ShowDialog(Me) <> Windows.Forms.DialogResult.OK Then
      7. Me.Close()
      8. Return
      9. End If
      10. If dlgLg.txtPassword.Text <> "geheim" Then
      11. MessageBox.Show("warnix!")
      12. Me.Close()
      13. Return
      14. End If
      15. End Using
      16. 'Hier der eigentliche Programm-Start
      17. Dim files = New DirectoryInfo("..\..\TestPictures").GetFiles
      18. FileInfoBindingSource.DataSource = files
      19. End Sub


      Aber ich hab sogar Filmchen gemacht, dass man sieht, wie mans zurecht-klicksen kann:
      Direktlink

      Eine ganz annere Anwendung aber desselben Dialog-Prinzips findet sich auf: formübergreifend gebundener Datensatz-Editier-Dialog
      Dateien
      • PictureViewer.zip

        (260,91 kB, 318 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „ErfinderDesRades“ ()

      In Ergänzung zu den Ausführungen vom @ErfinderDesRades noch ein paar weiterführende Infos zum DialogResult.
      Ein modaler Dialog wird beendet, wenn in einer Dialog-Prozedur sein DialogResult gesetzt wird, sogar dann, wenn der Wert dabei nicht verändert wird:
      Mit diesem Hauptprogramm:

      VB.NET-Quellcode

      1. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      2. Using dlg = New Form2
      3. Dim res = dlg.ShowDialog
      4. MessageBox.Show(res.ToString)
      5. End Using
      6. End Sub
      und diesem Dialog mit 3 Button, Button1 => OK-Button, Button2 => Cancel-Button (beide ohne Code) und Button3:

      VB.NET-Quellcode

      1. Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
      2. Me.DialogResult = Windows.Forms.DialogResult.Ignore
      3. 'Me.DialogResult = Windows.Forms.DialogResult.OK
      4. End Sub
      Wird der Dialog gar nicht erst angezeigt und sofort mit DialogResult = Ignore bzw. DialogResult = OK (Kommentarzeichen entfernen) beendet.

      VB.NET-Quellcode

      1. Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
      2. 'Me.DialogResult = Windows.Forms.DialogResult.Ignore
      3. Dim xxx = Test()
      4. End Sub
      5. Private Function Test() As Integer
      6. Me.DialogResult = Windows.Forms.DialogResult.Ignore
      7. Return 42
      8. End Function
      Wird das DialogResult in einer Prozedur gesetzt, ist das Resultat identisch.
      Auch wenn dieser Aufruf nicht in der Form_Load erfolgt, wird der Dialog beendet:

      VB.NET-Quellcode

      1. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
      2. 'Me.DialogResult = Windows.Forms.DialogResult.Ignore
      3. Dim xxx = Test()
      4. End Sub

      Dieses Verhalten gilt allerdings nur für einen modalen Dialog.
      Ein nicht modaler Dialog wird so nicht beendet!
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

      Noch ein paar Bemerkungen zum Aufruf von

      VB.NET-Quellcode

      1. .Show()
      2. .Show(Me)
      3. .ShowDialog()
      4. .ShowDialog(Me)
      Bei nicht modalen Dialogen verhindert der Aufruf .Show(Me), dass der Dialog hinter das aufrufende Fenster (Parent) geklickt werden kann, was bei .Show() möglich ist.
      Bei modalen Dialogen sieht man den Unterschied erst, wenn beim aufrufenden Fenster (Parent) .TopMost = True gesetzt ist:
      Ist beim aufrufenden Fenster .TopMost = False gesetzt, gibt es keinen Unterschied zwischen .ShowDialog() und .ShowDialog(Me).
      Aber
      bei einem aufrufenden Fenster mit .TopMost = True verschwindet der mit .ShowDialog() aufgerufene Dialog hinter dem Hauptfenster, falls er nicht selbst auf .TopMost = True gesetzt wurde.
      Bei einem Aufruf mit .ShowDialog(Me) erscheint der modale Dialog wie gewohnt vor aufrufenden Fenster.
      Im Zweifelsfalle ist es also sinnvoll, Dialoge mit .Show(Me) bzw. .ShowDialog(Me) aufzurufen.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!

      RodFromGermany schrieb:


      ... Allerdings muss dabei aufgepasst werden, ob die jeweilige Variable schon instanziert bzw. der Dialog schon wieder geschlossen wurde. So ist gesichert, dass bei Button-Klick diese eine Instanz stets wieder angezeigt werden kann .…

      Da habe ich mich soeben gefragt, ja OK wie löse ich dies am besten - da diesbezüglich kein Code vorgeschlagen wird.

      Die Lösung hat RodFromGermany an sich schon im folgenden Beitrag beschrieben: Problem nach Entladen einer Form - System.ObjectDisposedException

      Dieser Code kann dann noch durch Prüfung auf Form.Created ergänzt werden:

      VB.NET-Quellcode

      1. Public Class Form1
      2. Dim frm2 As Form2 = Nothing
      3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      4. If frm2 Is Nothing OrElse frm2.IsDisposed Then frm2 = New Form2
      5. If Not frm2.Created Then
      6. frm2.Show(Me)
      7. Else
      8. frm2.Focus()
      9. End If
      10. End Sub
      11. End Class

      Nachtrag zum Beitrag unten von RodFromGermany:

      Wenn wir es jetzt sehr genau nehmen, dann stand "frm2.Created " auch noch nirgendwo. ;)

      Nachtrag vom 29.04.16: Mittlerweile kann eine weitere simple Beispielanwendung (a hoch 2 = b) inkl. Events zu diesem Thema angeschaut werden: Text wird nicht in Textdatei geschrieben

      Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „Thias“ ()

      Thias schrieb:

      da diesbezüglich kein Code vorgeschlagen wird
      Doch, in Post #1.
      Allerdings wird da einer bereits angezeigten Form nicht explizit der Fokus gegeben, das ist bei Deinem Code neu. Danke.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!
      Noch ein paar weiterführende Infos zum DialogResult.
      Wenn einem Button im Designer ein von None verschiedener DialogResult-Wert zugewiesen wurde, wird beim Drücken dieses Buttons der Dialog automatisch beendet und dieser DialogResult-Wert zurückgegeben.
      Nun kann es aber vorkommen, dass z.B. beim Prüfen von Eingaben festgestellt wird, dass der Dialog nicht verlassen werden soll.
      Dies ist einfach möglich, indem der DialogResult-Property des Dialoges der Wert DialogResult.None zugewiesen wird.
      Beispiel: Form1 mit Button, Form2 mit OK-Button und CheckBox.
      Spoiler anzeigen
      Die Form2 kann über den OK-Button nur dann verlassen werden, wenn die CheckBox gecheckt ist:

      VB.NET-Quellcode

      1. Public Class Form1
      2. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      3. Using dlg = New Form2
      4. dlg.ShowDialog()
      5. End Using
      6. End Sub
      7. End Class

      VB.NET-Quellcode

      1. Public Class Form2
      2. Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      3. Me.Button1.DialogResult = Windows.Forms.DialogResult.OK
      4. Me.AcceptButton = Me.Button1
      5. End Sub
      6. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      7. If Not Me.CheckBox1.Checked Then
      8. MessageBox.Show("Checkbox nicht gecheckt")
      9. Me.DialogResult = DialogResult.None
      10. End If
      11. End Sub
      12. End Class
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!
      @Coldfire Wahrscheinlich hast Du den Sinn der Ausführungen vom @ErfinderDesRades und mir zum Aufruf von modalen Dialogen und der Verwendung der Property DialogResult nicht vollständig erfasst.
      Das Framework bietet die Funktionalität, Button und Dialogverhalten bei Beendigung zu verknüpfen:
      DialogResult.OK <-> AcceptButton
      und
      DialogResult.Cancel <-> CancelButton.
      Alt+F4 und das Beenden-X liefern in diesem Kontext DialogResult.Cancel.
      Wenn bei einem Dialog Werte zu prüfen sind, passiert das nicht, wenn der Dialog abgebrochen wurde, sondern beim Akzeptieren der Eingaben, dies passiert im Click-Ereignis des AcceptButton's.
      Im FormClosing-Event sollten Dinge überprüft werden, die unabhängig vom DialogResult sind wie z.B. nicht bereinigte Ressourcen: Hardware-Zugriffe oder Zugriffe auf native DLLs.
      Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
      Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
      Ein guter .NET-Snippetkonverter (der ist verfügbar).
      VB-Fragen über PN / Konversation werden ignoriert!