Results 1 to 11 of 11

Thread: [Resolved] How to detect when a child form as been closed?

  1. #1

    Thread Starter
    Lively Member Blupig's Avatar
    Join Date
    Apr 2008
    Posts
    118

    Arrow [Resolved] How to detect when a child form as been closed?

    Hello, in my program I need a way to detect when a child form has been closed. I've created an MdiWindowList thing (but in the form of a toolbar with buttons, similarly to the Windows taskbar), and everything works great, but the method I have for refreshing the taskbar's windows has to be called when a child form closes. I've trying calling the method from the child's Form_Closing event, but there's a slight lag so whenever there's more than 1 window open the first one gets skipped (then the others close properly, since the taskbar is being refreshed first before the form actually closes).

    I don't think this should be terribly hard, I just can't figure it out myself.

  2. #2
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: How to detect when a child form as been closed?

    Your method is correct (except that you should probably handle the FormClosed event instead, the closing can still be canceled in the FormClosing event), but you're probably doing something wrong.

    I would expect that you have a method that refreshes the 'mdi window list toolbar', let's call it "RefreshMdiToolbar". When you show a new child form, you can use AddHandler to listen for the event, and update the toolbar there.
    vb.net Code:
    1. Private Sub AddChildForm()
    2.    Dim f As New ChildForm1()
    3.    f.MdiParent = Me
    4.    AddHandler f.FormClosed, AddressOf ChildFormClosed
    5.    f.Show()
    6. End Sub
    7.  
    8. Private Sub ChildFormClosed(ByVal sender As Object, ByVal e As EventArgs)
    9.    Me.RefreshMdiToolbar()
    10. End Sub

    That's what I would try. It sounds like you are already doing this though. Can you explain your last line more in detail? I have no idea what you mean by 'there's a slight lag and the first one gets skipped'. Also show your code, at least the relevant parts.

  3. #3

    Thread Starter
    Lively Member Blupig's Avatar
    Join Date
    Apr 2008
    Posts
    118

    Re: How to detect when a child form as been closed?

    I'm actually using your code for a custom MdiWindowList, but tailored to a toolstrip. I took the liberty of grabbing an image to maybe help you determine my problem:



    With 3 windows, there are 3 toolstrip buttons. But, when I close any window for the first time, the buttons don't disappear. Now, I made an event to use your "RefreshWindowList" method when the mouse goes over the toolstrip. So, when I do that, the toolstrip refreshes accordingly and everything is ok. Now, if I close a second window, a button disappears. Starting with the 3 windows, I'd have 1 window left (after closing the first 2) but I'd be left with 2 buttons. If I close the last one, I'd now be left with 1 button which would, once again, disappear if I put my mouse over it because of the instantaneous refreshing.

    I "cheated" and had a fix for it, I basically just put in a timer with an interval of 10 ms that would refresh the toolstrip automatically each interval. But, that would cause some extreme rendering/buffering issues when there would be more buttons than what the toolstrip could contain, causing them to go off in that separate menu off to the right side. The menu would flash and no items within it could be selected. This is because of the refreshing every 10 ms. Besides the buffering issues, everything else worked fine though.

    So, essentially what I believe I'm trying to find now, is how to have a constant watch for closed forms, but to be able to determine which form it is. I tried AddHandler not only in the place where my form gets added, but also in the Form_Closing and Form_Closed events in the actual child form. All 3 methods did not work, the AddHandler in the parent form did nothing and the two in the child form had the same effect as everything else with the "skipping".

  4. #4
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: How to detect when a child form as been closed?

    Show your code for the AddHandler in the parent form. If you call the RefreshWindowList method in the FormClosed event handler of any MDI form, then it should work. The only reason when it won't work is that the MDI form is still in the MdiChildren collection at that point. In that case, you could set a global variable to the closed form, and don't add a button for that form.

    Something like this
    vb.net Code:
    1. Private _ClosingForm As ChildForm1
    2.  
    3. Private Sub AddChild()
    4.    Dim f As New ChildForm1()
    5.    f.MdiParent = Me
    6.    AddHandler f.FormClosed, AddressOf ChildFormClosed
    7.    f.Show()
    8. End Sub
    9.  
    10. Private Sub ChildFormClosed(ByVal sender As Object, ByVal e As EventArgs)
    11.    _ClosingForm = DirectCast(sender, ChildForm1)
    12.    Me.RefreshWindowList()
    13. End Sub
    14.  
    15. Private Sub RefreshWindowList()
    16.    For Each f As Form In Me.MdiChildren
    17.       If f IsNot _ClosingForm Then
    18.          'add to toolbar
    19.       End If
    20.    Next
    21. End Sub

    You could also pass the closing form to the RefreshWindowList method, it doesn't really matter.

  5. #5

    Thread Starter
    Lively Member Blupig's Avatar
    Join Date
    Apr 2008
    Posts
    118

    Re: How to detect when a child form as been closed?

    I've tried what you've just suggested but I still get the same "skipping" effect. Here's my AddHandler code:

    Code:
        Private Sub RunescapeToolStripMenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RunescapeToolStripMenuItem1.Click
            Dim F As New frmRunescape
            F.MdiParent = Me
            AddHandler F.FormClosed, AddressOf ChildFormClosed
            F.Show()
            RefreshWindows()
        End Sub
    
        Private Sub ChildFormClosed(ByVal sender As Object, ByVal e As EventArgs)
            Dim c As frmRunescape
            c = DirectCast(sender, frmRunescape)
            RefreshWindows()
    
        End Sub
    Is there some way to dynamically remove a form from an MdiChildren collection directly?

    EDIT: I think I know what the problem is. I added a pause to the AddHandler as well as the Form_Closed event for debugging . It seems that both events are not raised while the form is completely closed, but instead it's raised right before the form closes. So, by adding code into Form_Closed, it doesn't do anything since the form is still open and technically part of the MdiChildren collection.
    Last edited by Blupig; Aug 14th, 2010 at 10:00 PM.

  6. #6
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: How to detect when a child form as been closed?

    It's presumably your RefreshWindows method that's the issue. That's presumably the method that displays the window items yet you haven't shown us that code. I would guess that what's happening is that you're looping through a list that still contains the form that was just closed. In that case, simply test the IsDisposed property of each form in the list and ignore any that are True.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  7. #7

    Thread Starter
    Lively Member Blupig's Avatar
    Join Date
    Apr 2008
    Posts
    118

    Re: How to detect when a child form as been closed?

    Quote Originally Posted by jmcilhinney View Post
    It's presumably your RefreshWindows method that's the issue. That's presumably the method that displays the window items yet you haven't shown us that code. I would guess that what's happening is that you're looping through a list that still contains the form that was just closed. In that case, simply test the IsDisposed property of each form in the list and ignore any that are True.
    IsDisposed will never be true because 100% disposed forms are not in MdiChildren. For some reason, the form is not being completely disposed...

    Here's the RefreshWindows() sub I have:
    Code:
        Public Sub RefreshWindows()
    
            Dim mdiWindowCount As Integer = Me.MdiChildren.Count
            Dim menuItemWindowCount As Integer = Me.toolSwitch.Items.Count
    
            Dim item As ToolStripItem
            For i As Integer = menuItemWindowCount - 1 To 0 Step -1
                item = Me.toolSwitch.Items(i)
    
                If Not (item Is cmdClose _
                        Or item Is cmdDesktop _
                        Or item Is cmdRadio _
                        Or item Is ToolStripSeparator1 _
                        Or item Is ToolStripSeparator2) Then
                    Me.toolSwitch.Items.RemoveAt(i)
                End If
            Next
    
            If mdiWindowCount > 0 Then
                Dim menuItem As ToolStripButton
                Dim counter As Integer = 1
    
                For Each window As Form In Me.MdiChildren
    
                    If window.IsDisposed = True Then MsgBox("got one")
    
                    menuItem = New ToolStripButton
                    menuItem.Text = window.Text
                    menuItem.Tag = window
    
                    If window Is Me.ActiveMdiChild Then
                        menuItem.Checked = True
                    End If
    
                    AddHandler menuItem.Click, AddressOf ButtonClicked
    
                    Me.toolSwitch.Items.Insert(Me.toolSwitch.Items.Count, menuItem)
    
                    counter += 1
                Next
            End If
        End Sub
    This is really breaking my balls.
    Last edited by Blupig; Aug 14th, 2010 at 10:35 PM.

  8. #8
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: How to detect when a child form as been closed?

    I just tested and it does appear that things are a bit screwy with MDI child forms as far as when the FormClosed event is raised. How about you add a parameter to your RefreshWindows method for a form to ignore. When you loop through the MdiChildren you can then compare each one to that parameter and ignore it if they match?
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  9. #9

    Thread Starter
    Lively Member Blupig's Avatar
    Join Date
    Apr 2008
    Posts
    118

    Re: How to detect when a child form as been closed?

    I figured it out - I ended up declaring a variable as the MdiClient object, then made a handler to detect when a control is removed from it. The delegate I put in was RefreshWindows(), and now everything works like a charm. Thanks for all of you guys' help though

    Here's the code in case someone else has the same issue:
    Code:
            Dim client = Me.Controls.OfType(Of MdiClient).FirstOrDefault()
            AddHandler client.ControlRemoved, AddressOf RefreshWindowList

  10. #10
    Super Moderator jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    111,222

    Re: How to detect when a child form as been closed?

    Quote Originally Posted by Blupig View Post
    I figured it out - I ended up declaring a variable as the MdiClient object, then made a handler to detect when a control is removed from it. The delegate I put in was RefreshWindows(), and now everything works like a charm. Thanks for all of you guys' help though

    Here's the code in case someone else has the same issue:
    Code:
            Dim client = Me.Controls.OfType(Of MdiClient).FirstOrDefault()
            AddHandler client.ControlRemoved, AddressOf RefreshWindowList
    It won't actually make a difference in this case but you've got a bit of a logic flaw there. You're calling FirstOrDefault, which means get the first item in the collection or the default value for the type (i.e. Nothing for classes) if the collection is empty. You then go ahead and reference a member of the object returned by FirstOrDefault. That will throw an exception if FirstOrDefault returns Nothing so you're obviously assuming that it won't return Nothing. In that case, why bother with the default at all? You should just be calling First rather than FirstOrDefault.

    Further to that, the collection you're using should always and only contain one item. In that case, it's more appropriate to call Single than First.

    Like I said, none of this will make a difference to the end result but it's important to understand what is going on so that you do do the right thing when it will make a difference.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  11. #11
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    Re: How to detect when a child form as been closed?

    Quote Originally Posted by Blupig View Post
    EDIT: I think I know what the problem is. I added a pause to the AddHandler as well as the Form_Closed event for debugging . It seems that both events are not raised while the form is completely closed, but instead it's raised right before the form closes. So, by adding code into Form_Closed, it doesn't do anything since the form is still open and technically part of the MdiChildren collection.
    That's what I said in my previous post. The FormClosed event is raised, but the form is still in the MdiChildren collection so if you call the RefreshWindowList method in the FormClosed event then it will still add the 'closed' form.

    I also told you how to remedy this: keep track of the form that is being closed, either in a global variable or by passing it to the RefreshWindowList method.

    You were almost doing that, but not quite. You were declaring a local variable c which you cannot use outside that method. Also, you weren't checking whether the current form you are looping with (in the RefreshWindowList method) was the form that was being closed.

    Either way, I think handling the ControlRemoved event is even better, and you might want to handle the ControlAdded event too, instead of calling the method manually every time you show a new child form. This way you can have 36 different pieces of code where you add a new child form, but you'll call the method in just one place (in the ControlAdded event), instead of in 36 places separately.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width