Windows Forms mehrmals öffnen und stapeln

  • VB.NET
  • .NET (FX) 4.5–4.8

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

    Windows Forms mehrmals öffnen und stapeln

    Hey Ihr Lieben,

    ich habe folgendes Probem:

    ich möchte eine Benachrichtigung auf dem Desktop einblenden, die ich in als Windows Form gelöst hab. Die Form lässt sich dann über Parameter beim Aufrufen über die DeskNotification.ShowNotification(Title as String, etc...)-Methode konfigurieren. Damit man mehrere davon aufbekommt hab ich das ganze dann so deklariert:

    VB.NET-Quellcode

    1. Dim Benachrichtigung As New DeskNotification
    2. Benachrichtigung.ShowNotification(...)


    Es werden nun manchmal mehrere davon geöffnet und einige sind davon zum Bleiben gedacht. Und wie diese Notifications es so an sich haben, sollen die sich dann übereinander stapeln. Komme ich irgendwie an die bereits geöffneten ran um zu sehen wieviele es sind? Und kann man die dann auch irgendwie nachträglich modifizieren? Wäre ziemlich praktisch, dann könnten sie auch Live-Fortschritte darstellen.

    Schon mal vielen Dank im Vorraus für eure Hilfe,
    Liebe Grüße
    Hafreak :)
    Okay

    Ich hab das bis jetzt mal so gelöst:

    In der DesktopNotification hab ich folgendes hinzugefügt:


    VB.NET-Quellcode

    1. Public Property NotificationName As String = "DNOT"
    2. Public Property AlreadyOpen As Boolean = False
    3. Public Shared NotificationsList As List(Of DeskNotification)


    Ich habe festgestellt, dass man über die Application.OpenForms-Liste eine Form über Ihren Namen aufrufen kann.
    Deshalb weise ich in der ShowNotification noch einen Namen zu, der dann hinter ein "DNOT" (kurz für DeskNotification) kommt.

    Wenn man die Form über ShowNotification aufruft wird dann über ein Event die folgende Methode aufgerufen:

    VB.NET-Quellcode

    1. Public Sub NewNotification() Handles Me.NewNotificationOpened
    2. NotificationsList.Add(Application.OpenForms.Item(NotificationName))
    3. If Application.OpenForms.Item(NotificationName.Substring(0, 4)) IsNot Nothing Then
    4. AlreadyOpen = True
    5. End If
    6. End Sub


    Wenn er also eine Form findet die mit DNOT anfängt, weiß er, dass schon eine offen ist.

    Im Load steht dann:

    VB.NET-Quellcode

    1. If AlreadyOpen = True Then
    2. Me.LocationElseY -= (Me.Height) * NotificationsList.Count
    3. End If


    Und beim Schließen der Form:

    VB.NET-Quellcode

    1. Private Sub DeskNotification_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    2. NotificationsList.Remove(Application.OpenForms.Item(NotificationName))
    3. End Sub



    Jetzt funktioniert das leider gar nicht und es geht immer nur eine Meldung auf, selbst wenn man mehrere nacheinander aufruft (über die Benachrichtigung-Variable).

    und beim Schließen gibt das NotificationsList.Remove(...) eine Nullverweisaufnahme.

    Habt ihr irgendwelche Ideen?

    Hafreak schrieb:

    VB.NET-Quellcode

    1. NotificationsList.Remove(Application.OpenForms.Item(NotificationName))
    Was ganz genau soll diese Zeile machen?
    Nimm sie wieder raus.
    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!

    Hafreak schrieb:

    VB.NET-Quellcode

    1. Public Shared NotificationsList As List(Of DeskNotification)
    Wird die iwo mit New instanziiert?
    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!

    Hafreak schrieb:

    in einer anderen Klasse
    Brauchst Du nicht, deswegen is es ja Static.
    Aber Du musst mit New eine Instanz erstellen.
    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!
    ich würde überhaupt keine Extra NotificationsList verwalten.
    Application.OpenForms ist doch gut - wozu eine eigene Liste?

    Über

    VB.NET-Quellcode

    1. dim notifyItems = Application.OpenForms.OfType(Of DeskNotification)
    kannst du dir jederzeit die aktuell offenen DeskNotification-Dinger holen.
    Ich hab jetzt die Liste gelöscht und durch das ersetzt:

    VB.NET-Quellcode

    1. Dim notificationItems() = Application.OpenForms.OfType(Of DeskNotification)()


    Und den Load so umgeschrieben:

    VB.NET-Quellcode

    1. If notificationItems.Count > 1 Then
    2. Me.LocationElseY -= (Me.Height) * notificationItems.Count
    3. End If


    Jetzt lädt aber das Hauptfenster nicht in der ich die

    VB.NET-Quellcode

    1. Public Benachrichtigung As New DeskNotification


    deklariert hab.
    Bei der Deklaration bekomme ich eine "System.InvalidOperationException". Fehler beim Erstellen des Formular [...] Das Objekt des Typs "<OfTypeIterator>d__1`1[Kuf_VB.DeskNotification]" kann nicht in Typ "System.Object[]" umgewandelt werden.
    Ich bin nur ein einfacher W-Informatikstudent :D Ich hab keine Ahnung was Strict off sein soll und hab es auch noch nie benutzt :o Ich hab das nur einmal in nem Codebeispiel für Layered Windows gesehen, das war dann aber Strict On und ich hatte damit meine Probleme, weil Visual Studio es als Fehler markiert hat (stand nicht ganz oben im Code)

    Was den Typ angeht, schätze ich mal eine System.Collections.Generic.IEnumerable(Of DeskNotification) oder? Aber das sagt mir dafür trotzdem nichts

    Hafreak schrieb:

    IEnumerable(Of DeskNotification)
    Ich bin beeindruckt - ist fast richtig.
    Dieser Typ sollte dabei heraus kommen, aber du hast hinten an deine Deklaration die () drangemacht, wasses zu einer Array-Deklaration macht.
    Bei Strict On würde sowas nicht durchgehen, aber Strict Off versucht umzuwandeln, und stellt also erst zur Laufzeit fest:
    Das Objekt des Typs ... kann nicht in Typ ... umgewandelt werden.


    Dringende Empfehlung: Visual Studio - Empfohlene Einstellungen nicht nur als Unterhaltungs-Lektüre lesen, sondern machen, was da steht und ausführlichst begründet ist.
    Ach so okay, stimmt das war dumm.
    Wie komme ich denn an die Anzahl der Elemente der IEnumerable? Weil darum geht es ja...Weil einen Count oder sowas hat es anscheinend nicht.


    EDIT: Ich habs gefunden:

    VB.NET-Quellcode

    1. Dim numberOfNotifications As Integer = Application.OpenForms.OfType(Of DeskNotification).Count


    Jetzt zeigt er mir aber trotzdem nur eine Meldung an obwohl ich drei aufrufe:

    VB.NET-Quellcode

    1. Benachrichtigung.ShowNotification("Meldung1", "Nachrichtentext 1", "12325", My.Resources.PinnedIcon_32x32)
    2. Benachrichtigung.ShowNotification("Meldung2", "Nachrichtentext 2", "2231", My.Resources.Finished)
    3. Benachrichtigung.ShowNotification("Dritte Meldung mit langer Überschrift", "Kurzer Text", "0998", New Bitmap(1, 1), , False)


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

    Also wenn schon eine DeskNotification offen ist, dann macht er keine weitere auf, er macht dann irgendwie einfach nichts. Und wenn man dann die eine zu macht und eine neue auf, kommt das:

    Eine nicht behandelte Ausnahme des Typs "System.ObjectDisposedException" ist in System.Windows.Forms.dll aufgetreten.
    Zusätzliche Informationen: Auf das verworfene Objekt kann nicht zugegriffen werden.

    Hafreak schrieb:

    System.ObjectDisposedException
    Offensichtlich greifst Du auf ein Objekt zu, das bereits zerstört wurde.
    Verfolge also die History dieses Objekts oder frage beim Zugriff danach, ob es bereits disposed wurde:

    VB.NET-Quellcode

    1. If Not myObject.IsDisposed Then
    2. ' was tun
    3. End If
    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!
    In Ordnung, tut mir leid. Das ist der Teil des Codes den ich hierfür als relevant betrachte. Die XML Kommentare habe ich ausgelassen

    VB.NET-Quellcode

    1. Public Class DeskNotification
    2. #Region "Declarations and Procedures"
    3. Inherits ComponentFactory.Krypton.Toolkit.KryptonForm
    4. Private Event PinStateChanged As Action
    5. Private Event NewNotificationOpened As Action
    6. Public Enum NotificationLocation
    7. LowerRight
    8. UpperRight
    9. LowerLeft
    10. UpperLeft
    11. LocationElse
    12. End Enum
    13. Dim InitialOpacity As Double
    14. Dim PinState As String = "Unpinned"
    15. Dim FadeOutOn As Boolean = False
    16. Dim PropertyChanged As System.ComponentModel.INotifyPropertyChanged
    17. Dim notificationItems = Application.OpenForms.OfType(Of DeskNotification)()
    18. Dim numberOfNotifications As Integer = Application.OpenForms.OfType(Of DeskNotification).Count
    19. #Region "Properties"
    20. Public Property Title As String = "Title"
    21. Public Property NotificationText = "Notification"
    22. Public Property StartLocation As NotificationLocation = NotificationLocation.LowerRight
    23. Public Property LocationElseX = 0
    24. Public Property LocationElseY = 0
    25. Public Property NotificationOpacity = 0.8
    26. Public Property Timeout = 3500
    27. Public Property NotificationBackgroundColor As Color
    28. Public Property ShowImage As Boolean = False
    29. Public Property SourceImage As System.Drawing.Image = New Bitmap(1, 1)
    30. Public Property AutoClose As Boolean = False
    31. Public Property NotificationName As String = "DNOT"
    32. Public Property AlreadyOpen As Boolean = False
    33. #End Region
    34. Public Sub ShowNotification(Title As String, NotificationText As String, NotificationName As String, SourceImage As Bitmap, Optional AutoClose As Boolean = False, Optional ShowImage As Boolean = True, Optional StartLocation As NotificationLocation = NotificationLocation.LowerRight, Optional LocationElseX As Integer = 0, Optional LocationElseY As Integer = 0, Optional NotificationOpacity As Decimal = 0.8, Optional Timeout As Integer = 3500)
    35. Me.Title = Title
    36. Me.NotificationText = NotificationText
    37. Me.StartLocation = StartLocation
    38. Me.LocationElseX = LocationElseX
    39. Me.LocationElseY = LocationElseY
    40. Me.NotificationOpacity = NotificationOpacity
    41. Me.Timeout = Timeout
    42. Me.AutoClose = AutoClose
    43. Me.ShowImage = ShowImage
    44. Me.SourceImage = SourceImage
    45. Me.NotificationName = "DNOT" + NotificationName
    46. Me.Show()
    47. 'RaiseEvent NewNotificationOpened()
    48. End Sub
    49. #End Region
    50. Private Sub DesktopNotification_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    51. Me.Name = NotificationName
    52. 'Setting up background color
    53. If Not Me.NotificationBackgroundColor = Nothing Then
    54. Me.BackColor = Me.NotificationBackgroundColor
    55. Else
    56. Me.BackColor = SystemColors.Control
    57. End If
    58. 'Setting the window in front of all others
    59. Me.TopMost = True
    60. 'Setting up Properties
    61. TitleLbl.Text = Title
    62. NotificationTextTxtBox.Text = NotificationText
    63. Me.Opacity = NotificationOpacity
    64. Select Case StartLocation
    65. Case NotificationLocation.LowerRight
    66. Me.Location = New Drawing.Point(Screen.PrimaryScreen.WorkingArea.Width - Me.Width, Screen.PrimaryScreen.WorkingArea.Height - Me.Height)
    67. Case NotificationLocation.UpperRight
    68. Me.Location = New Drawing.Point(Screen.PrimaryScreen.WorkingArea.Width - Me.Width, 0)
    69. Case NotificationLocation.LowerLeft
    70. Me.Location = New Drawing.Point(0, Screen.PrimaryScreen.WorkingArea.Height - Me.Height)
    71. Case NotificationLocation.UpperLeft
    72. Me.Location = New Drawing.Point(0, 0)
    73. Case NotificationLocation.LocationElse
    74. Me.Location = New Drawing.Point(LocationElseX, LocationElseY)
    75. End Select
    76. If numberOfNotifications > 1 Then
    77. Me.LocationElseY -= (Me.Height) * numberOfNotifications
    78. End If
    79. TimeoutTimer.Interval = Timeout
    80. If AutoClose = True Then
    81. TimeoutTimer.Start()
    82. End If
    83. If ShowImage = False Then
    84. NotificationTextTxtBox.Location = New Drawing.Point(13, 44)
    85. Else
    86. PictureBox2.Visible = True
    87. NotificationTextTxtBox.Location = New Drawing.Point(86, 44)
    88. NotificationTextTxtBox.Size = New Drawing.Size(336, 71)
    89. If Not SourceImage Is Nothing Then
    90. PictureBox2.Image = SourceImage
    91. End If
    92. End If
    93. If AutoClose = False Then
    94. PinBtnPictBox.Image = New Bitmap(My.Resources.PinnedIcon2_32x32, 14, 14)
    95. Me.AutoClose = True
    96. Else
    97. PinBtnPictBox.Image = New Bitmap(My.Resources.PinIcon_32x32, 14, 14)
    98. Me.AutoClose = False
    99. End If
    100. CloseBtnPictBox.Image = New Bitmap(My.Resources.Close_32x32, 11, 11)
    101. End Sub


    Beim ersten Mal, mache ich eine Notification beim Laden des Hauptfensters auf, dann geht der Debugger erst ins ShowNotification(....), geht dann in der DeskNotification Klasse durch die ShowNotification Methode und dann durch die DeskNotification_Load Methode. Dann geht er zurück zum Hauptfenster und macht das Load fertig.

    Wenn man ein zweites aufmacht, geht er in der DeskNotification aber nur noch durch das ShowNotification und nicht mehr durch das Load. Ab dem Me.Show() geht er wieder zurück ins Hauptfenster.

    Macht man die Notification vorher zu, springt er an der selben Stelle, bei dem Me.Show() in den Fehler.

    Ich hoffe damit kann man jetzt mehr anfangen :)

    EDIT:
    Mir ist gerade nicht bewusst, wie man einen Beitrag an dieser Stelle zitieren kann, aber @RodFromGermany:

    Die Benachrichtigung Variable ist beim Aufruf eines zweiten Fensters tatsächlich disposed, er überspringt den Code in der If Anweisung einfach.

    Aber wie bringe ich ihm dann bei, dass ich ein neues Fenster haben will? Mehr als ein New DeskNotification zu deklarieren fällt mir nicht ein...

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

    Ich konnte das Problem lösen! :thumbsup:

    Ich deklariere jetzt die neue Form wenn eine gebraucht wird einfach jedes Mal neu anstatt einmal für die ganze Klasse:

    VB.NET-Quellcode

    1. Dim Notification = New DeskNotification
    2. Notification.ShowNotification("Meldung1", "Nachrichtentext 1", "12325", My.Resources.PinnedIcon_32x32)


    Das funktioniert soweit auch.

    Bloß legen die sich immer noch alle an die selbe Stelle und verdecken sich gegeseitig und stapeln sich nicht so übereinander auf.

    Wisst ihr vielleicht, angesichts des Quellcodes oben, was man da machen kann?

    EDIT:

    Ich möchte mich an dieser Stelle für meine Denkfaulheit entschuldigen!

    Ich hab einen Denkfehler eingebaut und die Me. LocationElseY festgelegt anstatt Me.Top

    Jetzt stapelt sich auch alles schön :D

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