Selbstgehäkeltes mdi-modul - passt das so?

  • VB.NET

Es gibt 44 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    @tragl Ich hatte dann die Button durch Menü-Einträge ersetzt und das TabControl ebenfalls oben angedockt.
    Möglicherweise muss das Menü der Childs das Menü des Parents vollständig enthalten.
    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).
    Programmierfragen über PN / Konversation werden ignoriert!
    @RodFromGermany also ich hab das nochmal von meinem Modul auf die Form gepackt, beide Varianten bewirken das Gleiche.
    Hast du noch eine Idee wie ich es schaffen kann, dass wenn z.B. Child1 bereits "offen" ist und ich es erneut öffnen will, dass es dann aktiviert wird?
    Das ist das Einzige was bei beiden Varianten nicht funzt leider :(
    Originaler (noch) Nichtskönner :D
    @tragl Wodurch zeichnet sich in Deiner Variante ein bereits offenes Child aus?
    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).
    Programmierfragen über PN / Konversation werden ignoriert!
    Vollzitat des direkten Vorposts an dieser Stelle entfernt ~VaporiZed

    also ich kann mit Child.IsMdiChild abfragen, ob's schon offen ist - weiß aber nicht wo und wie ich das einsetzen muss, dass dann eben das Child und somit das entsprechend
    bereits vorhandene TabPage aktiviert wird.

    Setze ich das wie im Code in Zeile 5 ein, kann ich mit Haltepunkt sehen dass bereits offen ist aber weder .activate, noch .bringtofront bewirkt da was :(
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Sub ShowMdiChild(mdiParent As Form, Child As Form, mdiTab As TabControl, Optional dts As DataSet = Nothing, Optional CtrlStyling As Boolean = False, Optional tv As TreeView = Nothing)
    2. _tabControl = mdiTab
    3. _mdiParent = mdiParent
    4. RegisterMdiHandle() ' Registrierung der Handle-Methoden
    5. If Child.IsMdiChild Then Child.Activate()
    6. With Child
    7. If .Visible Then
    8. .BringToFront()
    9. Else
    10. #If DEBUG Then
    11. 'nix
    12. #Else
    13. If Not tv Is Nothing Then
    14. tv.Visible = False
    15. tv.Dock = DockStyle.None
    16. End If
    17. #End If
    18. If Not dts Is Nothing Then dts.Register(.Self, True) ' Optional nur mit den Helpers von ErfinderDesRades
    19. If CtrlStyling = True Then ControlStyling.StyleDGV(.Self) ' Optional: eine Erweiterung von ErfinderDesRades
    20. .MdiParent = _mdiParent
    21. .MinimizeBox = False ' Optional: Minimize-Boxen auf Child-Forms sind unnötig
    22. .Show()
    23. End If
    24. End With
    25. End Sub

    Originaler (noch) Nichtskönner :D

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

    ErfinderDesRades schrieb:

    Um welche Probleme gehts hier momentan?


    Also im Beispiel öffne ich zuerst "Standort", dann "Fahrzeuge" - Fahrzeuge ist selektiert und geöffnet und wird angezeigt.
    Doppelklicke ich nun im Tree erneut auf "Standort" dann flackert zwar kurz der Bildschirm, es bleibt aber "Fahrzeuge" selektiert und angezeigt.

    Da hätte ich gerne, dass wenn der User erneut in Child öffnen will, obwohl das schon offen ist, das auch selektiert und angezeigt wird.
    Ich hoff' das konnte man verstehen ;)

    Edit: hier mal mein aktueller Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Public Module TabbedMdi
    3. 'TODO: bei erneutem Öffnen eines Childs das bereits geöffnete aktivieren
    4. Private _tabControl As TabControl
    5. Private _mdiParent As Form
    6. ''' <summary> Anzeigen einer ChildForm - Optional mit ControlStyling und Registrierung des DataSets </summary>
    7. <Extension>
    8. Public Sub ShowMdiChild(mdiParent As Form, Child As Form, mdiTab As TabControl, Optional dts As DataSet = Nothing, Optional CtrlStyling As Boolean = False, Optional tv As TreeView = Nothing)
    9. _tabControl = mdiTab
    10. _mdiParent = mdiParent
    11. RegisterMdiHandle() ' Registrierung der Handle-Methoden
    12. With Child
    13. If .Visible Then
    14. .BringToFront()
    15. Else
    16. #If DEBUG Then
    17. 'nix
    18. #Else
    19. If Not tv Is Nothing Then
    20. tv.Visible = False
    21. tv.Dock = DockStyle.None
    22. End If
    23. #End If
    24. If Not dts Is Nothing Then dts.Register(.Self, True) ' Optional nur mit den Helpers von ErfinderDesRades
    25. If CtrlStyling = True Then ControlStyling.StyleDGV(.Self) ' Optional: eine Erweiterung von ErfinderDesRades
    26. .MdiParent = _mdiParent
    27. .MinimizeBox = False ' Optional: Minimize-Boxen auf Child-Forms sind unnötig
    28. .Show()
    29. End If
    30. End With
    31. End Sub
    32. ''' <summary> Registriert die Handle-Methoden für die Steuerung der Child-Forms </summary>
    33. Private Sub RegisterMdiHandle()
    34. AddHandler _mdiParent.MdiChildActivate, AddressOf MdiOpen
    35. AddHandler _mdiParent.MdiChildActivate, AddressOf MdiActivate
    36. AddHandler _tabControl.SelectedIndexChanged, AddressOf MdiActivate
    37. End Sub
    38. ''' <summary> Stellt beim Schließen des Childs sicher, dass die entsprechende TabPage entfernt wird </summary>
    39. Private Sub MdiClose(sender As Object, e As FormClosedEventArgs)
    40. TryCast(TryCast(sender, Form).Tag, TabPage).Dispose()
    41. End Sub
    42. ''' <summary> Öffnen bzw. Selektieren der Childform </summary>
    43. Private Sub MdiOpen(sender As Object, e As EventArgs)
    44. If _mdiParent.ActiveMdiChild Is Nothing Then
    45. _tabControl.Visible = False
    46. Else
    47. _mdiParent.ActiveMdiChild.WindowState = FormWindowState.Maximized
    48. Dim tp As New TabPage(_mdiParent.ActiveMdiChild.Text)
    49. If _mdiParent.ActiveMdiChild.Tag Is Nothing Then
    50. tp.Tag = _mdiParent.ActiveMdiChild
    51. tp.Parent = _tabControl
    52. _tabControl.SelectedTab = tp
    53. _mdiParent.ActiveMdiChild.Tag = tp
    54. AddHandler _mdiParent.ActiveMdiChild.FormClosed, AddressOf MdiClose
    55. End If
    56. If Not _tabControl.Visible Then _tabControl.Visible = True
    57. End If
    58. End Sub
    59. Private Sub MdiActivate(sender As Object, e As EventArgs)
    60. If _tabControl.SelectedTab IsNot Nothing AndAlso _tabControl.SelectedTab.Tag IsNot Nothing Then TryCast(_tabControl.SelectedTab.Tag, Form).Select()
    61. End Sub
    62. End Module

    Bilder
    • unbenannt1.png

      230,96 kB, 544×145, 18 mal angesehen
    • unbenannt2.png

      59,5 kB, 541×121, 19 mal angesehen
    Originaler (noch) Nichtskönner :D

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „tragl“ ()

    tragl schrieb:

    Doppelklicke ich nun im Tree erneut auf "Standort" dann flackert zwar kurz der Bildschirm, es bleibt aber "Fahrzeuge" selektiert und angezeigt.
    Jo, das Problem habich behoben.



    Jetzt habich aber anderes Problem:
    Ich hab bischen mit Menustrips rumgespielt und finde, dass die MenuItems der ChildForms sich zwar in den Menustrip des MDIParents einmergen - aber der Menustrip bleibt trotzdem im ChildForm herumliegen - nur nun ohne Items.
    Was ich ziemlich deppert finde.
    Dateien
    • TabbedMdi00.zip

      (20,28 kB, 18 mal heruntergeladen, zuletzt: )

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

    tragl schrieb:

    obwohl das schon offen ist
    Wenn es nicht zu sehen ist, ist es entweder minimiert oder im Hintergrund.
    Für ersteres probierst Du .WindowState = FormWindowState.Normal,
    für letzteres

    VB.NET-Quellcode

    1. .TopMost = True
    2. .TopMost = False

    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).
    Programmierfragen über PN / Konversation werden ignoriert!

    ErfinderDesRades schrieb:

    aber der Menustrip bleibt trotzdem im ChildForm herumliegen - nur nun ohne Items.
    Was ich ziemlich deppert finde.

    meinst du die gelb markierte leere Stelle?


    RodFromGermany schrieb:

    Für ersteres probierst Du

    die Childs werden ja mit FormWindowState.Maximized aufgerufen, sollte daran also nicht liegen.
    Mit .topmost spiele ich mal rum, bzw. schaue mir heute abend mal die Änderung von EDR an - er hat's ja wohl schon gefixed :)
    Originaler (noch) Nichtskönner :D

    tragl schrieb:

    sollte daran also nicht liegen.
    Genau daran liegt es.
    Was passiert, wenn zwei Children mit .Maximized angezeigt werden?
    Das letztere siehst Du, das erste liegt dahinter.
    Also vertraue nicht auf das, was der @ErfinderDesRades möglicherweise nicht gemacht hat, sondern kriege es raus und mach es selbst. 8o
    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).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    sondern kriege es raus und mach es selbst.

    jaja, hast ja recht - aber ich wollte mir sowieso angucken wie genau er das gefixed hat, einfach code nehmen und reinpacken hab' ich keinen Lernerfolg von ;)
    Originaler (noch) Nichtskönner :D
    Also prinzipiell hat @ErfinderDesRades quasi die Sub

    VB.NET-Quellcode

    1. Private Sub MdiActivate(sender As Object, e As EventArgs)
    2. If _tabControl.SelectedTab IsNot Nothing AndAlso _tabControl.SelectedTab.Tag IsNot Nothing Then TryCast(_tabControl.SelectedTab.Tag, Form).Select()
    3. End Sub


    durch die folgende ersetzt:

    VB.NET-Quellcode

    1. Private Sub Tab_SelectedIndexChanged(sender As Object, e As EventArgs)
    2. DirectCast(_tabControl.SelectedTab?.Tag, Form)?.Select()
    3. End Sub


    Läuft bei mir nun auch endlich wie gewünscht, hab das nun wie folgt übernommen: (ich brauch' die Childs allerdings immer auf maximized :P ) und will das nicht
    auf der Form haben, sondern in jedem Projekt direkt griffbereit :D
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Public Module TabbedMdi
    3. Private _tabControl As TabControl
    4. Private _mdiParent As Form
    5. ''' <summary> Anzeigen einer ChildForm - Optional mit ControlStyling und Registrierung des DataSets, sowie verstecken des TreeView </summary>
    6. <Extension>
    7. Public Sub ShowMdiChild(mdiParent As Form, Child As Form, mdiTab As TabControl, Optional dts As DataSet = Nothing, Optional CtrlStyling As Boolean = False, Optional tv As TreeView = Nothing)
    8. _tabControl = mdiTab
    9. _mdiParent = mdiParent
    10. RegisterMdiHandle() ' Registrierung der Handle-Methoden
    11. With Child
    12. If .MdiParent Is mdiParent Then
    13. .Select()
    14. Else
    15. #If DEBUG Then
    16. 'nix
    17. #Else
    18. If Not tv Is Nothing Then
    19. tv.Visible = False
    20. tv.Dock = DockStyle.None
    21. End If
    22. #End If
    23. If Not dts Is Nothing Then dts.Register(.Self, True) ' Optional nur mit den Helpers von ErfinderDesRades
    24. If CtrlStyling = True Then ControlStyling.StyleDGV(.Self) ' Optional: eine Erweiterung von ErfinderDesRades
    25. .WindowState = FormWindowState.Maximized ' Je nach Anwendungsfall entweder alle Childs maximiert öffnen, oder nur das 1.
    26. 'If _mdiParent.MdiChildren.Count = 0 Then .WindowState = FormWindowState.Maximized
    27. .MdiParent = _mdiParent
    28. .MinimizeBox = False ' Optional: Minimize-Boxen auf Child-Forms sind unnötig
    29. .Show()
    30. mdiParent.ResumeLayout()
    31. End If
    32. End With
    33. End Sub
    34. ''' <summary> Registriert die Handle-Methoden für die Steuerung der Child-Forms </summary>
    35. Private Sub RegisterMdiHandle()
    36. AddHandler _mdiParent.MdiChildActivate, AddressOf MdiChildOpen
    37. AddHandler _tabControl.SelectedIndexChanged, AddressOf Tab_SelectedIndexChanged
    38. End Sub
    39. ''' <summary> Stellt beim Schließen des Childs sicher, dass die entsprechende TabPage entfernt wird </summary>
    40. Private Sub MdiChildClose(sender As Object, e As FormClosedEventArgs)
    41. DirectCast(DirectCast(sender, Form).Tag, TabPage).Dispose()
    42. End Sub
    43. ''' <summary> Prüft, ob das Child bereits existiert, falls nicht wird eine neue TabPage erstellt </summary>
    44. Private Sub MdiChildOpen(sender As Object, e As EventArgs)
    45. If _mdiParent.ActiveMdiChild Is Nothing Then
    46. _tabControl.Visible = False
    47. Else
    48. Dim tp = DirectCast(_mdiParent.ActiveMdiChild.Tag, TabPage)
    49. If tp Is Nothing Then
    50. tp = New TabPage(_mdiParent.ActiveMdiChild.Text)
    51. tp.Tag = _mdiParent.ActiveMdiChild
    52. _mdiParent.ActiveMdiChild.Tag = tp
    53. _tabControl.TabPages.Add(tp)
    54. AddHandler _mdiParent.ActiveMdiChild.FormClosed, AddressOf MdiChildClose
    55. If Not _tabControl.Visible Then _tabControl.Visible = True
    56. End If
    57. _tabControl.SelectedTab = tp
    58. End If
    59. End Sub
    60. ''' <summary> Ruft beim wechseln der TabPage das entsprechende Child auf und zeigt es an</summary>
    61. Private Sub Tab_SelectedIndexChanged(sender As Object, e As EventArgs)
    62. DirectCast(_tabControl.SelectedTab?.Tag, Form)?.Select()
    63. End Sub
    64. End Module


    Allgemeine Frage noch dazu: Wann werden die ? verwendet?
    Originaler (noch) Nichtskönner :D
    @tragl Die ? sind ein absolut genialer If-like-Konstrukt:
    Wenn das, was vor dem ? steht, Nothing ist, wird der Befehl abgebrochen.
    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).
    Programmierfragen über PN / Konversation werden ignoriert!
    danke, coole Sache 8o
    also wenn's aus irgendeinem Grund keine selektierte TabPage gibt, dann gibt's auch keine Exception oder kommt die trotzdem?

    Edit:

    ErfinderDesRades schrieb:

    Ich hab bischen mit Menustrips rumgespielt und finde, dass die MenuItems der ChildForms sich zwar in den Menustrip des MDIParents einmergen - aber der Menustrip bleibt trotzdem im ChildForm herumliegen - nur nun ohne Items.
    Was ich ziemlich deppert finde.


    Ein Workaround dazu wäre, dass du beim Öffnen des Childs in z.B. Form_Load oder Sub New folgendes einfügst:
    MenuStrip1.Visible = False wenn du den Namen des Strips weißt und eingeben willst, ansonsten:

    VB.NET-Quellcode

    1. Private Sub childForm1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    2. For Each mstrip In Me.Controls.OfType(Of MenuStrip)()
    3. mstrip.Visible = False
    4. Next
    5. End Sub


    Der Witz an der Sache ist, dass das eigentliche Menu trotzdem mit dem MainStrip gemerged wird ?( :D

    Edit2: Geht sogar bei meiner "allgemeinen Methode": (Zeile 26-28)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. ''' <summary> Anzeigen einer ChildForm - Optional mit ControlStyling und Registrierung des DataSets, sowie verstecken des TreeView </summary>
    2. <Extension>
    3. Public Sub ShowMdiChild(mdiParent As Form, Child As Form, mdiTab As TabControl, Optional dts As DataSet = Nothing, Optional CtrlStyling As Boolean = False, Optional tv As TreeView = Nothing)
    4. _tabControl = mdiTab
    5. _mdiParent = mdiParent
    6. RegisterMdiHandle() ' Registrierung der Handle-Methoden
    7. With Child
    8. If .MdiParent Is mdiParent Then
    9. .Select()
    10. Else
    11. #If DEBUG Then
    12. 'nix
    13. #Else
    14. If Not tv Is Nothing Then
    15. tv.Visible = False
    16. tv.Dock = DockStyle.None
    17. End If
    18. #End If
    19. If Not dts Is Nothing Then dts.Register(.Self, True) ' Optional nur mit den Helpers von ErfinderDesRades
    20. If CtrlStyling = True Then ControlStyling.StyleDGV(.Self) ' Optional: eine Erweiterung von ErfinderDesRades
    21. .WindowState = FormWindowState.Maximized ' Je nach Anwendungsfall entweder alle Childs maximiert öffnen, oder nur das 1.
    22. 'If _mdiParent.MdiChildren.Count = 0 Then .WindowState = FormWindowState.Maximized
    23. .MdiParent = _mdiParent
    24. .MinimizeBox = False ' Optional: Minimize-Boxen auf Child-Forms sind unnötig
    25. .Show()
    26. For Each mstrip In Child.Controls.OfType(Of MenuStrip)()
    27. mstrip.Visible = False
    28. Next
    29. mdiParent.ResumeLayout()
    30. End If
    31. End With
    32. End Sub
    Originaler (noch) Nichtskönner :D

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „tragl“ ()

    meine Variante:

    VB.NET-Quellcode

    1. Child.Controls.OfType(Of MenuStrip)().SingleOrDefault()?.Hide
    bzw. natürlich kommentiert:

    VB.NET-Quellcode

    1. 'idiotischerweise muss der ChildForm-Menustrip, dessen Items einerseits automatisch in den Parent-Menustrip
    2. ' eingemerged werden, andererseits explizit versteckt werden - sonst verbleibt ein leerer Menustrip auffm ChildForm
    3. Child.Controls.OfType(Of MenuStrip)().SingleOrDefault()?.Hide

    Hallöchen, ich hab' da nochmal ein Anliegen zu dem Thema.
    Ich hab auf meiner Main-Form (Parent für mdiChilds) ein ToolStripMenuItem, was deaktiviert wird sobald
    ein Child geöffnet wird -> wird geregelt über Private Sub frmMain_MdiChildActivate(sender As Object, e As EventArgs) Handles Me.MdiChildActivate
    klappt auch.

    Jetzt bräuchte ich noch ein Event, was abgefeuert wird wenn Childs geschlossen werden - was ich aber auf der frmMain plazieren müsste.
    Mit dem Event soll das deaktivierte ToolStripMenuItem wieder aktiv gesetzt werden, sobald der Child-Counter auf 0 ist.

    Gibt's da was?
    Alternativ hab ich schon versucht das im TabbedMdi-Modul einzubauen, aber er castet mir nicht "control" auf "toolstripmenuitem" und ich wüsste jetzt
    nicht, wie ich auf der frmMain nach toolstripmenuitems suchen sollte...

    Edit:
    Hiermit geht's leider nicht... (Zeile 5-7)

    VB.NET-Quellcode

    1. ''' <summary> Stellt beim Schließen des Childs sicher, dass die entsprechende TabPage entfernt wird </summary>
    2. Private Sub MdiChildClose(sender As Object, e As FormClosedEventArgs)
    3. DirectCast(DirectCast(sender, Form).Tag, TabPage).Dispose()
    4. If _mdiParent.MdiChildren.Count <= 1 Then
    5. For Each tsi In _mdiParent.Controls.OfType(Of ToolStripMenuItem)()
    6. If Not tsi.Enabled Then tsi.Enabled = True
    7. Next
    8. _tv.TVshow 'wenn das letzte Child geschlossen wird, TreeView einblenden
    9. End If
    10. End Sub
    Originaler (noch) Nichtskönner :D

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „tragl“ ()

    Also das tsi wird deaktiviert, sobald ein Child geöffnet wird.
    Und wenn der letzte MdiChild geschlossen wird, solls re-aktiviert werden.
    Werden die zeilen #5-7 durchlaufen?
    Ich vermute: Ja, nur er findet in den Controls kein ToolstripMenuItem.
    Haste ja schon gemerkt, dass ein ToolstripMenuItem kein Control ist. Deswegen kanner inne Controls auch keines finden.
    ToolstripMenuItems sind in Toolstrips untergebracht - du wirst also wohl einen Toolstrip auf deim Form haben - wenn nicht gar merhere.
    Hint: MenuStrips sind zB Toolstrips. Aber ContextMenuStrips sind auch Toolstrips.

    Was mich auch wundert, dass du das tsi erfolgreich deaktivieren kannst, aber es zum re-aktivieren scheinbar nicht wiederfindest. wie findest du es denn, wenn du es deaktivierst?
    Das Deaktivieren mache ich auf der frmMain:

    VB.NET-Quellcode

    1. Private Sub frmMain_MdiChildActivate(sender As Object, e As EventArgs) Handles Me.MdiChildActivate
    2. disableEnableMaChange()
    3. End Sub
    4. Private Sub disableEnableMaChange()
    5. Dim countChilds = Me.MdiChildren.Count
    6. If Not countChilds = 0 Then
    7. tsImgChangeMA.Enabled = False : tsMitarbeiterWechseln.Enabled = False
    8. Else
    9. tsImgChangeMA.Enabled = True : tsMitarbeiterWechseln.Enabled = True
    10. End If
    11. End Sub


    Die Sub disableEnableMaChange würde ich auch gerne aufrufen, wenn das letzte Child geschlossen wird.. ich weiß aber nicht über welches Event ich das
    feuern kann aus der frmMain raus..

    Deshalb war die Idee, das über das Modul TabbedMDI über die Sub MdiChildClose zu regeln, wo auch die TabPages disposed werden und nach
    Schließen des letzen Childs der TreeView wieder sichtbar wird. Klar, ich könnte jetzt das entsprechende TSI auch noch an das Modul als Parameter übergeben
    aber dann wird's irgendwann zu lang. Ist es ja jetzt eigentlich schon Public Sub ShowMdiChild(mdiParent As Form, Child As Form, mdiTab As TabControl, Optional dts As DataSet = Nothing, Optional CtrlStyling As Boolean = False, Optional tv As TreeView = Nothing)
    Originaler (noch) Nichtskönner :D