Stay inside MDI Client Area
-
In MDI applications, it is a common occurance for the user to open several different forms. However, even if one form is closed, the "offset" used to place the subsequent forms is remembered, so each form is gradually shifted down and to the right until it shifts partially or fully outside of the MDI client area. I'd like to ensure that the MDI clients are always completely within the MDI client area upon first being opened. I have come up with a hack involving the OnVisibleChanged event and two helper functions (listed below), but is there a more graceful way of doing this?
' In each form Protected Overrides Sub OnVisibleChanged(ByVal e As System.EventArgs) EnsureInsideMDIArea(Me) End Sub ' Helper functions in a module Public Function GetMDIClientRect(ByVal frm As Form) As Rectangle Dim pr As Rectangle = Rectangle.Empty For Each c As Control In frm.Controls If TypeOf c Is MdiClient Then pr = c.ClientRectangle Next Return pr End Function Public Sub EnsureInsideMDIArea(ByVal frm As Form) ' Ignore if not an MDI client If Not (frm.MdiParent Is Nothing) Then ' Get parent rect Dim pr As Rectangle = GetMDIClientRect(frm.MdiParent) Dim r, b As Integer r = frm.Size.Width + frm.Left b = frm.Size.Height + frm.Top If r > pr.Right Or b > pr.Bottom Then frm.Top = 0 frm.Left = 0 End If End If End Sub
-
In MDI applications, it is a common occurance for the user to open several different forms. However, even if one form is closed, the "offset" used to place the subsequent forms is remembered, so each form is gradually shifted down and to the right until it shifts partially or fully outside of the MDI client area. I'd like to ensure that the MDI clients are always completely within the MDI client area upon first being opened. I have come up with a hack involving the OnVisibleChanged event and two helper functions (listed below), but is there a more graceful way of doing this?
' In each form Protected Overrides Sub OnVisibleChanged(ByVal e As System.EventArgs) EnsureInsideMDIArea(Me) End Sub ' Helper functions in a module Public Function GetMDIClientRect(ByVal frm As Form) As Rectangle Dim pr As Rectangle = Rectangle.Empty For Each c As Control In frm.Controls If TypeOf c Is MdiClient Then pr = c.ClientRectangle Next Return pr End Function Public Sub EnsureInsideMDIArea(ByVal frm As Form) ' Ignore if not an MDI client If Not (frm.MdiParent Is Nothing) Then ' Get parent rect Dim pr As Rectangle = GetMDIClientRect(frm.MdiParent) Dim r, b As Integer r = frm.Size.Width + frm.Left b = frm.Size.Height + frm.Top If r > pr.Right Or b > pr.Bottom Then frm.Top = 0 frm.Left = 0 End If End If End Sub
Graceful is in the eye of the beholder. If what you wrote works for you, great! Another method, though, would be to put this kind of code entirely in the MDI Parent and when you create the new child form, you can get it's size and make sure that it is inside the MDI Parent's client window without iterating through all the controls...
' In MDI Parent
Private Sub MenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItem2.Click
Dim newChild As New frmChild
newChild.MdiParent = Me' You must show the form before it will receive its Top and Left values. If you send ' the new form to EnsureChildInClientArea first, its Top and Left will always be 0,0. newChild.Show() EnsureChildInClientArea(newChild) End Sub
Private Sub EnsureChildInClientArea(ByRef frmChild As Form)
Dim r, b As Integer
r = frmChild.Width + frmChild.Left
b = frmChild.Height + frmChild.Top
If r > Me.ClientSize.Width Or b > Me.ClientSize.Height Then
frmChild.Top = 0
frmChild.Left = 0
End If
End SubRageInTheMachine9532
-
Graceful is in the eye of the beholder. If what you wrote works for you, great! Another method, though, would be to put this kind of code entirely in the MDI Parent and when you create the new child form, you can get it's size and make sure that it is inside the MDI Parent's client window without iterating through all the controls...
' In MDI Parent
Private Sub MenuItem2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItem2.Click
Dim newChild As New frmChild
newChild.MdiParent = Me' You must show the form before it will receive its Top and Left values. If you send ' the new form to EnsureChildInClientArea first, its Top and Left will always be 0,0. newChild.Show() EnsureChildInClientArea(newChild) End Sub
Private Sub EnsureChildInClientArea(ByRef frmChild As Form)
Dim r, b As Integer
r = frmChild.Width + frmChild.Left
b = frmChild.Height + frmChild.Top
If r > Me.ClientSize.Width Or b > Me.ClientSize.Height Then
frmChild.Top = 0
frmChild.Left = 0
End If
End SubRageInTheMachine9532
That would almost work. We'd need an
Application.DoEvents
after thenewChild.Show
because the top/left are not set until the new form is actually visible (very frustrating trying to figure THAT out...), and we'd actually need to check theMdiClient
size, not just theClientSize
, or else status bars and what not would not be taken into account. The biggest problem I have with the current solution is that when the form is created, it shows at the original location for 1/60th of a second, and the flicker looks bad. -
That would almost work. We'd need an
Application.DoEvents
after thenewChild.Show
because the top/left are not set until the new form is actually visible (very frustrating trying to figure THAT out...), and we'd actually need to check theMdiClient
size, not just theClientSize
, or else status bars and what not would not be taken into account. The biggest problem I have with the current solution is that when the form is created, it shows at the original location for 1/60th of a second, and the flicker looks bad.It was quick and dirty, but it worked. No intensive testing on it though... It was just an idea! :) RageInTheMachine9532