neelbak
asked on
Creating an MDI application to behave/look like a SDI application - 500 points!
I'm trying to create an MDI application that looks and behaves like a SDI application. I've gotten a partial implementation to work, but having problems with the sizing and location of the child forms. Also, I'm having problems disposing of the child forms correctly.
Here is what i'm trying to do.
There is just one menubar/toolbar and window that is consistent throughout the application. Some restrictions I have are that in order to save memory usage, I only want one child form to be created at a time and only when it is the visible form. When the user selects another button from the toolbar/menubar it then disposes/closes/frees up the resources (somehow) and creates the new form that was selected.
The child forms should also fill in the entire window (even upon resizing). I also can not show any scrollbars.
I'm new to MDI application desgin in VB.NET. I'm looking for a comprehensive example.
I hope this was clear enough. If any question or clarifications are needed, let me know.
I need this soon, so 500 points!
Here is what i'm trying to do.
There is just one menubar/toolbar and window that is consistent throughout the application. Some restrictions I have are that in order to save memory usage, I only want one child form to be created at a time and only when it is the visible form. When the user selects another button from the toolbar/menubar it then disposes/closes/frees up the resources (somehow) and creates the new form that was selected.
The child forms should also fill in the entire window (even upon resizing). I also can not show any scrollbars.
I'm new to MDI application desgin in VB.NET. I'm looking for a comprehensive example.
I hope this was clear enough. If any question or clarifications are needed, let me know.
I need this soon, so 500 points!
ASKER
I implemented the code you had, but I'm having two problems.
For every new page that I switch to, the form does not appear at the same location. It creates a cascading effect where each subsequent page/form loaded, moves it's start coordinates down 10 by 10 pixels from the top-left of the previous form.
Also, once I close a form, I can never get back to it. I'm not sure why. It just won't load.
I'm not sure how I should have each form initialized either. At the moment, I have the forms initialized as such.
Please let me know if you see anything wrong.
Below are snippets of my code:
'------------------------- ---------- ---------- ---------- ---------- ---------- ---------- -
' Variables on Form
Private WithEvents fMDIClient As System.Windows.Forms.MdiCl ient
Friend withevents fpchildForm1 as childForm1 = Nothing
Friend withevents fpchildForm2 as childForm2 = Nothing
'------------------------- ---------- ---------- ---------- ---------- ---------- ---------- -
' Mainform settings called from New() Constructor after IniitializeComponent()
Private Sub InitializeMainForm()
Me.AutoScale = False
Me.VScroll = False
Me.HScroll = False
Me.AutoScroll = False
Me.IsMdiContainer = True
End Sub
'------------------------- ---------- ---------- ---------- ---------- ---------- ---------- -
Private Sub ChangeChild(ByVal newPage as integer)
Select case newPage
case 0:
if fpChildForm1 is nothing then
fpChildForm1 = new childForm1
InstantiateChild(fpChildFo rm1)
end if
case 1:
if fpChildForm2 is nothing then
fpChildForm2 = new childForm2
InstantiateChild(fpChildFo rm2)
end if
End Select
End Sub
'------------------------- ---------- ---------- ---------- ---------- ---------- ---------- -
Public Sub InstantiateChild(ByRef setForm As Form)
'set general properties
setForm.BackColor = Color.White
setForm.FormBorderStyle = FormBorderStyle.None
setForm.AutoScroll = False
setForm.WindowState = FormWindowState.Normal
setForm.ControlBox = False
setForm.ShowInTaskbar = False
'set child-parent properties
setForm.MdiParent = Me
setForm.Size = Me.fMDIClient.Size
setForm.Show()
setForm.Activate()
End Sub
'------------------------- ---------- ---------- ---------- ---------- ---------- ---------- -
Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
For i = 0 To Me.Controls.Count - 1
If Me.Controls(i).GetType.ToS tring = "System.Windows.Forms.MdiC lient" Then
fMDIClient = CType(Me.Controls.Item(i), MdiClient)
Exit For
End If
Next
End Sub
For every new page that I switch to, the form does not appear at the same location. It creates a cascading effect where each subsequent page/form loaded, moves it's start coordinates down 10 by 10 pixels from the top-left of the previous form.
Also, once I close a form, I can never get back to it. I'm not sure why. It just won't load.
I'm not sure how I should have each form initialized either. At the moment, I have the forms initialized as such.
Please let me know if you see anything wrong.
Below are snippets of my code:
'-------------------------
' Variables on Form
Private WithEvents fMDIClient As System.Windows.Forms.MdiCl
Friend withevents fpchildForm1 as childForm1 = Nothing
Friend withevents fpchildForm2 as childForm2 = Nothing
'-------------------------
' Mainform settings called from New() Constructor after IniitializeComponent()
Private Sub InitializeMainForm()
Me.AutoScale = False
Me.VScroll = False
Me.HScroll = False
Me.AutoScroll = False
Me.IsMdiContainer = True
End Sub
'-------------------------
Private Sub ChangeChild(ByVal newPage as integer)
Select case newPage
case 0:
if fpChildForm1 is nothing then
fpChildForm1 = new childForm1
InstantiateChild(fpChildFo
end if
case 1:
if fpChildForm2 is nothing then
fpChildForm2 = new childForm2
InstantiateChild(fpChildFo
end if
End Select
End Sub
'-------------------------
Public Sub InstantiateChild(ByRef setForm As Form)
'set general properties
setForm.BackColor = Color.White
setForm.FormBorderStyle = FormBorderStyle.None
setForm.AutoScroll = False
setForm.WindowState = FormWindowState.Normal
setForm.ControlBox = False
setForm.ShowInTaskbar = False
'set child-parent properties
setForm.MdiParent = Me
setForm.Size = Me.fMDIClient.Size
setForm.Show()
setForm.Activate()
End Sub
'-------------------------
Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim i As Integer
For i = 0 To Me.Controls.Count - 1
If Me.Controls(i).GetType.ToS
fMDIClient = CType(Me.Controls.Item(i),
Exit For
End If
Next
End Sub
in sub instantiate child change this:
Public Sub InstantiateChild(ByRef setForm As Form)
'set general properties
setForm.BackColor = Color.White
setForm.FormBorderStyle = FormBorderStyle.None
setForm.AutoScroll = False
setForm.WindowState = FormWindowState.Normal
setForm.ControlBox = False
setForm.ShowInTaskbar = False
'set child-parent properties
setForm.MdiParent = Me
setForm.Size = Me.fMDIClient.ClientSize 'instead of size, this prevents scrollbars and makes sure your childform is exactly the same size as the inner clientarea.
setForm.StartPosition = FormStartPosition.Manual 'force the start position to manual. This prevents the typical windows behaviour of cascade open
setForm.Top = 0 'set the top and left to 0, making sure the child start from the upper left corner.
setForm.Left = 0 'set the top and left to 0, making sure the child start from the upper left corner.
setForm.Show()
setForm.Activate()
End Sub
Also add code to change child in order to dispose of the other form:
Private Sub ChangeChild(ByVal newPage As Integer)
Select Case newPage
Case 0
If fpchildForm1 Is Nothing Then
fpchildForm1 = New Form1
InstantiateChild(CType(fpc hildForm1, Form))
End If
'add code to dispose of the other form
If Not fpchildForm2 Is Nothing Then
fpchildForm2.Close()
fpchildForm2.Dispose()
fpchildForm2 = Nothing
End If
Case 1
If fpchildForm2 Is Nothing Then
fpchildForm2 = New Form2
InstantiateChild(CType(fpc hildForm2, Form))
End If
'add code to dispose of the other form
If Not fpchildForm1 Is Nothing Then 'check if an instance of the other form is open
fpchildForm1.Close()
fpchildForm1.Dispose()
fpchildForm1 = Nothing
End If
End Select
End Sub
Public Sub InstantiateChild(ByRef setForm As Form)
'set general properties
setForm.BackColor = Color.White
setForm.FormBorderStyle = FormBorderStyle.None
setForm.AutoScroll = False
setForm.WindowState = FormWindowState.Normal
setForm.ControlBox = False
setForm.ShowInTaskbar = False
'set child-parent properties
setForm.MdiParent = Me
setForm.Size = Me.fMDIClient.ClientSize 'instead of size, this prevents scrollbars and makes sure your childform is exactly the same size as the inner clientarea.
setForm.StartPosition = FormStartPosition.Manual 'force the start position to manual. This prevents the typical windows behaviour of cascade open
setForm.Top = 0 'set the top and left to 0, making sure the child start from the upper left corner.
setForm.Left = 0 'set the top and left to 0, making sure the child start from the upper left corner.
setForm.Show()
setForm.Activate()
End Sub
Also add code to change child in order to dispose of the other form:
Private Sub ChangeChild(ByVal newPage As Integer)
Select Case newPage
Case 0
If fpchildForm1 Is Nothing Then
fpchildForm1 = New Form1
InstantiateChild(CType(fpc
End If
'add code to dispose of the other form
If Not fpchildForm2 Is Nothing Then
fpchildForm2.Close()
fpchildForm2.Dispose()
fpchildForm2 = Nothing
End If
Case 1
If fpchildForm2 Is Nothing Then
fpchildForm2 = New Form2
InstantiateChild(CType(fpc
End If
'add code to dispose of the other form
If Not fpchildForm1 Is Nothing Then 'check if an instance of the other form is open
fpchildForm1.Close()
fpchildForm1.Dispose()
fpchildForm1 = Nothing
End If
End Select
End Sub
ASKER
Sorry it took a while to implemented the solution you gave me. Got forced onto working on something else. The solution worked great in switching the forms and disposing them. Setting the child form startupposition to manual solved my problem with the cascading open. However, I still have one problem.
When I switch forms, I can still see the titlebar show up for a split second before it disappears. This happens even though I have the child form borderstyle = None
Any suggestions on how to get rid of this?
i also implemented some doublebuffering hopeing to eliminate this. I did this by adding the following lines of code after InitializeComponents
Me.SetStyle(ControlStyles. AllPaintin gInWmPaint Or _
ControlStyles.UserPaint Or _
ControlStyles.DoubleBuffer , True)
Unfortunately, this didn't help.
When I switch forms, I can still see the titlebar show up for a split second before it disappears. This happens even though I have the child form borderstyle = None
Any suggestions on how to get rid of this?
i also implemented some doublebuffering hopeing to eliminate this. I did this by adding the following lines of code after InitializeComponents
Me.SetStyle(ControlStyles.
ControlStyles.UserPaint Or _
ControlStyles.DoubleBuffer
Unfortunately, this didn't help.
Doublebuffering doesn't work... I'll see if I can find a solution, but for now I have none.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
You are amazing. I'm not exactly sure why the slight changes work, but it does.
One thing that I did not like when I switched to an MDI implementation is the sunken border that is created. Is there a way to remove that so the child form appears to be part of the Parent form and more like a SDI application?
One thing that I did not like when I switched to an MDI implementation is the sunken border that is created. Is there a way to remove that so the child form appears to be part of the Parent form and more like a SDI application?
I'm afraid it can't be done. The mdiclient is always sunken and you can't change it's borderstyle to none which gets rid of the 3D style.
Maybe you can try drawing a rectangle on top of the mdiform with the same color as the backcolor of the child form to overlay the borders, or you can try playing with the paint event of the mdiclient overriding it and drawing yourself.
The only other way I can think of is by using a normal form instead and then displaying the child at a certain point with a certain size, like that of a panel for example. You could then calculate the location of the panel in screen coördinates (= panel.pointtoscreen(panel. location)) and get the form to display on top of it. This way you can cut of the borders yourself.
The main problem with this is that you would have to create a class that keeps track of the childs, displays them, moves and resizes them and so on. In other words you would be building your own mdi/mdiclient object.
Maybe you can try drawing a rectangle on top of the mdiform with the same color as the backcolor of the child form to overlay the borders, or you can try playing with the paint event of the mdiclient overriding it and drawing yourself.
The only other way I can think of is by using a normal form instead and then displaying the child at a certain point with a certain size, like that of a panel for example. You could then calculate the location of the panel in screen coördinates (= panel.pointtoscreen(panel.
The main problem with this is that you would have to create a class that keeps track of the childs, displays them, moves and resizes them and so on. In other words you would be building your own mdi/mdiclient object.
If you want to use a normal form instead of an mdi try this code:
Make a form called MainForm and put a panel called MDIPanel on it and make it fill the form (dock fill). You can add multiple panels if you want to put controls on the mainform or just the one panel if you want to fill up the entire mainform with your childs.
Make sure the panel has its borderstyle set to none!
Next use this sub to call your childforms:
Private Sub Initform(ByVal f As Form)
Dim of As Form
For Each of In Me.OwnedForms
'free resources by unloading the childforms, thus making sure there is only one child at any given time.
Me.RemoveOwnedForm(of)
of.Dispose()
of = Nothing
Next
'Force toplevel to false and borderstyle to none to allow a form to have another form as parent.
'You have to do this in code at runtime. Just setting the properties of the childs at designtime is not enough
f.TopLevel = False
f.FormBorderStyle = FormBorderStyle.None
f.Parent = Me
'add setting for look and feel of the child as you wish
f.BackColor = Color.White
'Force the startposition in manual
f.StartPosition = FormStartPosition.Manual
'Get the top left corner of the panel and set the top left of the form to the same position
Dim p As Point = MyPanel.Location
f.location = p
'make the child the same size as the panel
f.Size = MyPanel.ClientSize
'show the form and force it to the top. Otherwise if you use more than one panel, you wont see the childform as it will be hidden beneath the panels.
f.Show()
f.BringToFront()
End Sub
Make a form called MainForm and put a panel called MDIPanel on it and make it fill the form (dock fill). You can add multiple panels if you want to put controls on the mainform or just the one panel if you want to fill up the entire mainform with your childs.
Make sure the panel has its borderstyle set to none!
Next use this sub to call your childforms:
Private Sub Initform(ByVal f As Form)
Dim of As Form
For Each of In Me.OwnedForms
'free resources by unloading the childforms, thus making sure there is only one child at any given time.
Me.RemoveOwnedForm(of)
of.Dispose()
of = Nothing
Next
'Force toplevel to false and borderstyle to none to allow a form to have another form as parent.
'You have to do this in code at runtime. Just setting the properties of the childs at designtime is not enough
f.TopLevel = False
f.FormBorderStyle = FormBorderStyle.None
f.Parent = Me
'add setting for look and feel of the child as you wish
f.BackColor = Color.White
'Force the startposition in manual
f.StartPosition = FormStartPosition.Manual
'Get the top left corner of the panel and set the top left of the form to the same position
Dim p As Point = MyPanel.Location
f.location = p
'make the child the same size as the panel
f.Size = MyPanel.ClientSize
'show the form and force it to the top. Otherwise if you use more than one panel, you wont see the childform as it will be hidden beneath the panels.
f.Show()
f.BringToFront()
End Sub
ASKER
It's unfortunate that microsoft doesn't make visualizations easy.
I have a related question that I posted that you could answer, if you do the points are yours What if I used a usercontrol instead? Are there any issues that would be different than if I used a form?
I have some code that will work for a usercontrol... just don't know if there are resource issues with it.
https://www.experts-exchange.com/questions/21419596/Differences-between-a-UserControl-and-Form.html
I have a related question that I posted that you could answer, if you do the points are yours What if I used a usercontrol instead? Are there any issues that would be different than if I used a form?
I have some code that will work for a usercontrol... just don't know if there are resource issues with it.
https://www.experts-exchange.com/questions/21419596/Differences-between-a-UserControl-and-Form.html
I tried to answer the other question as best as I could. Basically there won't be much difference in resources, only in available functionality.
ASKER
Thanks for all the help... I have everything working great now!
No problem, glad to have been of service.
Make a function to dispose of the childforms and call it each time when a button opening an new child is clicked.
an example:
Private WithEvents fMDICLient As System.Windows.Forms.MdiCl
Private Sub MDFORM_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
'loop through the controls on the mdi to find the mdiclient
Dim i As Integer
For i = 0 To Me.Controls.Count - 1
If Me.Controls(i).GetType.ToS
fMDICLient = CType(Me.Controls.Item(i),
'as you have declared it with events you can now add handlers to pick up the mdiclient-events who are otherwise hidden.
AddHandler fMDICLient.MouseEnter, AddressOf MEATMaster_MouseEnter
AddHandler fMDICLient.MouseLeave, AddressOf MEATMaster_MouseLeave
'When we have the client, no need to go on looping through the controls
Exit For
End If
Next
End Sub
'form each of your buttons where you whish to open a new form, call this function:
private sub InstantiateChild(childform
'first we dispose of the current child, if any
Try
Me.MdiChildren(0).Close()
Catch
End Try
'add the form as mdichild, set the size to the clientsize of the mdiclient, thus filling the screen, show it and make sure it becomes active and gets the focus.
childform.MdiParent = Me
childform.Size = fMDICLient.ClientSize
childform.Show()
childfrom.Activate()
end sub