Link to home
Start Free TrialLog in
Avatar of neelbak
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!
Avatar of Fenris_Lokisson
Fenris_Lokisson

In the mdi form make a variabel calle mdiclient which is the subform that holds all childs. Using this you can resize your childform to the size of mdiclient and thus fill up the entire form.
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.MdiClient

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.ToString = "System.Windows.Forms.MdiClient" Then
        fMDICLient = CType(Me.Controls.Item(i), MdiClient)
       
        '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 as form)
'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
 
Avatar of neelbak

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.MdiClient
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(fpChildForm1)
              end if

     case 1:
              if fpChildForm2 is nothing then
                    fpChildForm2 = new childForm2
                    InstantiateChild(fpChildForm2)
              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.ToString = "System.Windows.Forms.MdiClient" Then
                fMDIClient = CType(Me.Controls.Item(i), MdiClient)
                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(fpchildForm1, 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(fpchildForm2, 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
Avatar of neelbak

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.AllPaintingInWmPaint Or _
                            ControlStyles.UserPaint Or _
                            ControlStyles.DoubleBuffer, True)

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
Avatar of Fenris_Lokisson
Fenris_Lokisson

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of neelbak

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?
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.
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

Avatar of neelbak

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 tried to answer the other question as best as I could. Basically there won't be much difference in resources, only in available functionality.
Avatar of neelbak

ASKER

Thanks for all the help...  I have everything working great now!
No problem, glad to have been of service.