[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 660
  • Last Modified:

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!
0
neelbak
Asked:
neelbak
  • 8
  • 5
1 Solution
 
Fenris_LokissonCommented:
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
 
0
 
neelbakAuthor Commented:
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
0
 
Fenris_LokissonCommented:
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
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
neelbakAuthor Commented:
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.
0
 
Fenris_LokissonCommented:
Doublebuffering doesn't work... I'll see if I can find a solution, but for now I have none.
0
 
Fenris_LokissonCommented:
This was a hard one to get done.
Took me a while to find out what caused the problem.

If you want, you can even get rid of the friends withevents childforms, minimizing the use of resources. The mdichild can be accessed as me.mdichildren(0) and from there you can do anything you want. I fyou want to capture events in your mdi, just addhandlers to the events you want to pick up or make a single friends withevents form and assign the mdichild to it.

OK, here goes:

First add one line of code to the loadevent of your childforms:

private sub form_load (sender as object, e as eventargs) handles mybase.load
  me.controlbox = false
end sub

Next change the changechild sub to remove the current mdichildforms as such:

Private Sub ChangeChild(ByVal newPage As Integer)
    'loop trough all childs and dispose of them, freeing up resources
    Dim f As Form
    For Each f In Me.MdiChildren
      f.Close()
      f.Dispose()
    Next

    'instantiate a form of the type wanted.
    Select Case newPage
      Case 0
        f = New childform1
      Case 1
        f = New childform2
    End Select
    'Call the code to show the form
    InstantiateChild(f)
    'dispose of this one as you now have an mdichild(0) instead wich can be accessed
    'It allows you to free up even more resources and get rid of the friend withevents forms
    f = Nothing

  End Sub

Finally change the Instantiatechild sub as such:

Public Sub InstantiateChild(ByRef setForm As Form)
    'set general properties
    setForm.BackColor = Color.White
    setForm.StartPosition = FormStartPosition.Manual
    'set child-parent properties
    setForm.MdiParent = Me
    setForm.Show()
    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.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.

    Me.ActivateMdiChild(setForm)


  End Sub
0
 
neelbakAuthor Commented:
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?
0
 
Fenris_LokissonCommented:
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.
0
 
Fenris_LokissonCommented:
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

0
 
neelbakAuthor Commented:
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.

http://www.experts-exchange.com/Programming/Programming_Languages/Dot_Net/VB_DOT_NET/Q_21419596.html

0
 
Fenris_LokissonCommented:
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.
0
 
neelbakAuthor Commented:
Thanks for all the help...  I have everything working great now!
0
 
Fenris_LokissonCommented:
No problem, glad to have been of service.
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 8
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now