StrataFrame Forum

MdiWindowListItem

http://forum.strataframe.net/Topic5186.aspx

By Ivan George Borges - 12/11/2006

Hiya.

I am shamelessly trying to copy "someone's" idea of a MainForm.Tongue

My intention is to get rid of the MenuStrip and use the ThemedToolStrip instead. So far, so good. But now I got to the "Window" item, which will show the opened MDI windows list.

Now I would need to have a "Window" ToolStripDropDownButton to behave in the same way as the MenuStrip MdiWindowListItem. Would that be too difficult to implement?

Cheers.

By Trent L. Taylor - 12/11/2006

I am shamelessly trying to copy "someone's" idea of a MainForm.

I wonder where I have seen that done before Wink

Now I would need to have a "Window" ToolStripDropDownButton to behave in the same way as the MenuStrip MdiWindowListItem. Would that be too difficult to implement?

It really isn't too difficult.  We actually use a list instead of updating the DropDown menu, which is easier and takes less time.  In either case, you just need to "wrap" the loading and closing of your forms.  We have a menu system that wraps all of our menu calls and we create a handler when the form is created.  Also, when the form is created we add it to the list and pass along the window handle.  When the forms "FormClosed" event fires, we then remove the item from the list.

The above is a screen shot from our medical system and shows how we implement the Windows Panel.  Ours is a little more complicated because we actually have a legacy VFP session within the MDI environment as well as the .NET forms.  So both of them have to show up in this panel which is a little more cumbersome.  But for .NET only forms, this is much easier because you are directly in control of the forms.

One other option is to just monitor the MDIChildren property of the MDI form or client.  The MDI client or form has a property called MdiChildren which is a list of all of the visible forms:

Me.MdiChildren

In any case, there are a number of ways to monitor the forms and place them in a collection and deal with them.  If this doesn't get you started let me know and I will give you some more snippets.

By Ivan George Borges - 12/11/2006

I see you copied from the same person!!!  BigGrin

Yeah, I found your main form pretty sharp, so I'm trying the same.

I got the idea, will work on it. If I feel like completely lost, I will cry for help. I'm looking for the proper way to "wrap" the loading and closing of the forms ... just need to find a nice peace of paper. Hehe

Thanks Trent.

By Trent L. Taylor - 12/11/2006

If I feel like completely lost, I will cry for help.

Hehe Sounds good.

just need to find a nice peace of paper.

I don't know that one exists.  Here is a sample method to load up all of your client forms in the MDI.

Declare the form collection

''' <summary>
    ''' Collection of all of the forms
    ''' </summary>
    ''' <remarks></remarks>
    Private _Forms As New System.Collections.Generic.Dictionary(Of Integer, System.Windows.Forms.Form)

Create a reusable method to load a child form

''' <summary>
    ''' A reusable method that allows forms to be launched.  This will reside in your
    ''' MDI form.
    ''' </summary>
    ''' <param name="FormType"></param>
    ''' <remarks></remarks>
    Public Sub LoadForm(ByVal FormType As System.Type)
        '-- Establish Locals
        Dim loForm As System.Windows.Forms.Form

        '-- Create the form
        loForm = CType(Activator.CreateInstance(FormType), Form)

        '-- Associate the new form with the MDI form
        loForm.MdiParent = Me

        '-- Add a handler to the form close
        AddHandler loForm.FormClosed, AddressOf HandleFormClosed

        '-- This is a good place to update your open form collection
        _Forms.Add(loForm.Handle.ToInt32(), loForm)

        '-- Update the list or menu

        '-- Show the form
        loForm.Show()
    End Sub

Create a handler method that is used to trap the FormClosed event.  This will be assigned in the LoadForm() method

''' <summary>
    ''' Handles the closing of the form
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub HandleFormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs)
        '-- Establish Locals
        Dim loForm As System.Windows.Forms.Form

        '-- The sender will be the form.  See if the form is in the collection
        If _Forms.ContainsKey(CType(sender, Form).Handle.ToInt32()) Then
            '-- Get the form reference
            loForm = _Forms(CType(sender, Form).Handle.ToInt32())

            '-- Remove the handler
            RemoveHandler loForm.FormClosed, AddressOf HandleFormClosed

            '-- Remove from the collection
            _Forms.Remove(CType(sender, Form).Handle.ToInt32())

            '-- Update your list or menu
        End If
    End Sub

Sample method showing how to call the LoadForm method

''' <summary>
    ''' Just a sample of how to call the LoadForm method
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub SampleLoadingForm()
        LoadForm(GetType(MyForm))
    End Sub
By Ivan George Borges - 12/11/2006

wow ... thanks Trent. I had the LoadForm already, but the Handler got me stuck.

I will try to populate a list just like you did. You have put the Themed Containers in a docked form on the left, is that it?

By Trent L. Taylor - 12/11/2006

You have put the Themed Containers in a docked form on the left, is that it?

That's it.  Just to improve performance on the ThemedContainer, set the MinimizeBodyBehavior to Snap instead of Scroll if you are going to allow the container to be opened and closed.

By Michael Reese - 12/15/2006

Hey Trent,

Nice, I have been attempting to do the same. Would be nice to have the sample code in a small projectSmile

Michael

By Ivan George Borges - 12/15/2006

Can't believe it, Trent ... I did it!!!

It is even activating the item when clicked in the ListView.

I can go and enjoy my Friday night now.

THANKS A LOT.

By Trent L. Taylor - 12/15/2006

This form looks very familiar ... I just can't understand what it says BigGrin

Good job, I really like the way it looks!

By Trent L. Taylor - 12/15/2006

Seriously...it looks great! 
By Michael Cobb - 6/14/2007

This is a good thread.  This may be slightly off topic but I wasn't sure where to post this question. 

I have a main MDI form with the following method that is used to launch a child form.

    Private Sub LaunchForm(ByVal FormType As System.Type, Optional ByVal AllowMultiple As Boolean = True)
        '-- Look to see if the form is already loaded - bring to front
        If AllowMultiple = False Then
            For Each loCurForm As Form In Me.MdiChildren
                If Not loCurForm.InvokeRequired Then    ' Can access the form directly
                    If loCurForm.GetType() Is FormType Then
                        If loCurForm.WindowState = FormWindowState.Minimized Then
                            loCurForm.WindowState = FormWindowState.Maximized
                        End If

                        loCurForm.BringToFront()
                        Exit Sub
                    End If
                End If
            Next
        End If

        '-- Establish Locals
        Me.Cursor = Cursors.WaitCursor

        Dim loForm As Form
        loForm = CType(Activator.CreateInstance(FormType), Form)

        '-- Set the MDI
        loForm.MdiParent = Me

        '-- Show the form
        loForm.Show()

        Me.Cursor = Cursors.Default
    End Sub

The child form has a control which needs to open a new child form in the main MDI form.  Any ideas about how to accomplish this?

Thanks for looking!

By Trent L. Taylor - 6/14/2007

I think that the best thing to do would be to expose the MDI client...or....if your main form just has the MDIContainer property set you can reference it directly.  When the form is launched it will have a reference to the parent form in the ParentForm property which you can type and reference.  You may need to make your LauncForm method public or Friend so it can be accessed though.

DirectCast(Me.ParentForm, MainForm).LaunchForm(GetType(MyChildForm))

If you are using an MDIClient control and manually adding it to the form, then you can just expose it through a public property and basically do the same thing:

DirectCast(Me.ParentForm, MainForm).MDIClientProperty.LaunchForm(GetType(MyChildForm))
By Michael Cobb - 6/15/2007

Thanks Trent.  This works perfectly!
By Trent L. Taylor - 6/15/2007

Glad it worked for you Smile
By StarkMike - 6/21/2007

Hi guys,

A couple of questions.... I am using a picture that you used earlier in the post... how did you create that MDI with that control panel on the left?

am i correct in assuming that if the user were to click or double click on the name of a form in the list in the control panel it would show that form?

and second, using the code you provided earlier in this post... how would I create navigation buttons to utilize this code... lets say that I wanted navigation buttons in the MDI to be able to navigate through all the child forms... yknow the basics... first, previous, next, last.

This is a really cool concept! I'm eager to implement it. BigGrin

By Trent L. Taylor - 6/22/2007

Well, basically this is just a docked Panel on the main form which we custom render and then add the logic we need for MDI Window Activation.  One thing that you can do is create an MDIClient class and then add that class as a control to the main form rather than setting the MdiContainer property....it just gives you more flexibility.

This entire explanation would be more of a tutorial rather than a quick post here...and in fact, this is one of the very things that we cover during class.  I know you guys haven't had a chance to make it to class in the past, but if it were at all possible, I think that you guys would gain a lot of first hand experience on items just like this if you were able to make it to the next class Smile 

By Teddy Jensen - 7/11/2007

Ivan,

Your main form looks really good. I too was first thinking of a toolstrip dropdown that would show the open windows.  Now i have decided to use a panel like yours and the one from Practice Studio, so i can use it to show some more info. I do however have issues with this (read - it doesn't look the way i want it)

Can you describe how you did your panel and lists?

TIA.

/Teddy

By Trent L. Taylor - 7/11/2007

Really all you need to do is set the MdiParent property on the parent form to False.  Drop a FlowLayoutPanel (or ThemedPanel) on the form and dock it left.  Next, in your main form, define an MdiClient class (it is a control) as a private and manually add it to the form.  This MdiClient will be where all of there client forms will appear, but the panels docking will be left intact.

Private _MdiClient As New MdiClient()


Public Sub New()
    _MdiClient.BackColor = Color.White
    Me.Controls.Add(_MdiClient)
End Sub

By Teddy Jensen - 7/11/2007

Hi Trent,

Thanks - i got it now. My problem was that i set the text of an employee form to the name of the employee after it is loaded from a browsedialog. So i wanted the new text of the form to be renamed/refreshed in the list.

The rest was just cosmetic customations of the properties.

/Teddy

By Ivan George Borges - 7/11/2007

Hi Teddy.

I'm glad Trent could help you, he got all this from me... oh, wait, I think the orther of the words are not set properly...

Unsure

By Edhy Rijo - 3/30/2008

H all,

I am using the LoadForm method posted here to load the forms, but I would like to automate this method by passing the form name stored in a Tag property of a menu item or a DevExpress RibbonControl Item.

The item will have the name of the form as string like this "frmProducers" but the LoadForm method is expecting a System.Type parameter instead of string, how can I either convert the Item.Tag string containing the form name as a System.Type expected by the LoadForm method posted here?

Private Sub LoadForm(ByVal FormType As System.Type)

'-- Establish Locals

Dim loForm As System.Windows.Forms.Form

'-- Create the form

loForm = CType(Activator.CreateInstance(FormType), System.Windows.Forms.Form)

'-- Associate the new form with the MDI form

loForm.MdiParent = Me

'-- Add a handler to the form close

AddHandler loForm.FormClosed, AddressOf HandleFormClosed

'-- This is a good place to update your open form collection

_Forms.Add(loForm.Handle.ToInt32(), loForm)

'-- Update the list or menu

'-- Show the Form

loForm.Show()

End Sub

The whole idea is to either type the form name in the Item.Tag property or read it from a database to load all the Items and update the Item.Tag which will make a generic single call to the LoadForm.

Thanks.

By StrataFrame Team - 3/31/2008

You can use System.Type.GetType(typeNameAsString) to convert a string to a System.Type.  You'll need to store more than just "frmFormName" in the tag of the form, however, because the Type.GetType() static method requires the full name of the class.

Type.GetType("MyNamespace.frmMyFormName")

That will be what you'll have to give it.

Also, it might be the case that that method will not be able to resolve the name because your form type exists in another assembly.  If that's the case, then you'll need to give it the fully qualified assembly name.  It's basically the full name with a comma and the assembly name right after it like this:

"MyNamespace.frmMyFormName, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

That's the difference between the Type.GetType() static method and the GetType() keyword (the keyword turns blue when you type it).  If you use the keyword, it will always resolve because the reference is resolved and at compile-time.  So, you might want to create a method that will resolve the names for you and uses the keyword to get the type.  Like this:

Public Shared Function ResolveFormType(ByVal formName As String) As Type
    Select Case formName
        Case "frmForm1"
            Return GetType(frmForm1)

        Case "frmForm2"
            Return GetType(frmForm2)

        Case "frmForm3"
            Return GetType(frmForm3)

    End Case
End Function

If you use this method, then the string can be any arbitrary value, as long as it's unique and you can assign it a type to return from the method.

By Edhy Rijo - 3/31/2008

Ben Chase (03/31/2008)
You'll need to store more than just "frmFormName" in the tag of the form, however, because the Type.GetType() static method requires the full name of the class.

Type.GetType("MyNamespace.frmMyFormName")

Hi Ben,

Thanks for the explanation.  Using

Type.GetType("MyNamespace.frmMyFormName")
does give me the results I was looking for.

If I were to use the ReolveForm solution, I would then have to make an entry in the Case statement for every form I add to the project and all I am trying to do is to have all forms identified in a Item.Tag so I don't have to manually remember to keep adding it to the Case statement.  Thanks again!!!

By Chan - 4/8/2008

Hi,

I tried the code snippet and convert it to C# as below. I face problem that, the value of loForm.handle is changed in HandleFormClosed event handler. Any ideas? Thank you



private void LaunchForm(System.Type FormType)

{

//-- Establish Locals

Form loForm;

//-- Create the form

loForm = (Form)Activator.CreateInstance(FormType);

//-- Set the MDI parent

loForm.MdiParent = this;

int lnHandle = (int)loForm.Handle;

loForm.FormClosed += this.HandleFormClosed;

_Forms.Add(lnHandle, loForm);

imgWindows.Images.Add(loForm.Text, loForm.Icon);

lvWindows.Items.Add(lnHandle.ToString(), loForm.Text, imgWindows.Images.Count - 1);

//-- Show the form

loForm.Show();

}



private void HandleFormClosed(object sender, FormClosedEventArgs e)

{

System.Windows.Forms.Form loForm;

loForm = (Form)sender;

int lnHandle = (int)loForm.Handle;

if (_Forms.ContainsKey(lnHandle))

{

loForm = _Forms[lnHandle];

loForm.FormClosed -= HandleFormClosed;

imgWindows.Images.RemoveByKey(loForm.Text);

lvWindows.Items.RemoveByKey(lnHandle.ToString());

_Forms.Remove(lnHandle);

}

}
By Chan - 4/8/2008

Hi,

May I know what control/container are you using to drop window and user panel on? I tried to use flowlayout but its docking cause themetoolstrip unable to dock to top with full screen width.



Please advice.



Thank you
By Trent L. Taylor - 4/8/2008

Handle the FormClosing event instead of the FormClosed event...that way you will still have a handle.
By Mike Tomlin - 4/15/2008

I'm trying the VB version of this technique and I too am getting back a different handle to that which the child window was assigned on creation. I've tried changing to formclosing but the handle still changes. Any thoughts?

Thanks

Mike

By Trent L. Taylor - 4/15/2008

The form handle won't change after it has been assigned.  If you are running in an MDI environment, then you can handle the OnParentChanged event and save off the handle at that point (I recommend against this).  You should ALWAYS be able to reference the Me.Handle of the form and it will return the handle to which it is presently assigned, which is the only one that you should ever deal with.  This is standard WinForms logic...once a handle is assigned it should never be changed unless the parent changes...that may be the ONLY time that it would change. 

Now it is another thing entirely if the handle has already been released.  In that case you just need to handle an event further up the chain to perform your closing logic.

By Edhy Rijo - 4/15/2008

Just to confirm,

I'm also getting back a different handle to than which the child window was assigned on creation.

And also I've tried changing to formclosing but the handle still changes.

By Trent L. Taylor - 4/15/2008

OK...different than what?  You have mentioned that you are getting back a different handle...but what are you comparing the handle to? 
By Trent L. Taylor - 4/15/2008

Oh....nevermind, you are using that older logic on the window handles being stored off in the collection...gotcha.  I will see if I can reproduce.
By Trent L. Taylor - 4/15/2008

Since we no longer use this logic I hadn't noticed that Vista changes the handle several times through the life of the form.  So to get around this, if you still intend to use window handle, then you will need to override the OnHandleChanged method to trap that event so that you can update your collection logic.

Overrides Protected Sub OnHandleChanged(...)
    '-- Add your logic to update the collections
End Sub

However, I would recommend against using the window handle at this point and add a Guid property to your BaseForm that uniquely identifies the form:

Public Class MyBaseForm
     Inherits MicroFour.StrataFrame.UI.Windows.Forms.StandardForm

   Private _FormId As String = System.Guid.NewGuid().ToString()

   Public Readonly Property FormId As String
        Get
             Return _FormId
        End Get
   End Property
End Class

Then you can enumerate the Forms collection of the MDI to find your form and then activate it (this code would be referencing the MDI  form):

For each f as Form In Me.MdiChildren
    If DirectCast(f, MyBaseForm).FormId = MyLookupId Then
        '-- You found the form
    End If
Next

If you take this approach you will be much safer across multiple platforms and won't be reliant upon the window handle.

By Edhy Rijo - 4/15/2008

Trent L. Taylor (04/15/2008)

For each f as Form In Me.MdiChildren
    If DirectCast(f, MyBaseForm).FormId = MyLookupId Then
        '-- You found the form
    End If
Next

If you take this approach you will be much safer across multiple platforms and won't be reliant upon the window handle.

Trent, I hate to ask, but using the GUID logic, where is the MyLookupID value coming from?

By Trent L. Taylor - 4/15/2008

You would be passing it into some method.  If you look further up the food-chain on this thread, you will see that they have a collection that stores off the open forms.  So when the form is loaded, you would add this guid to the collection with probably a title or something else so that it could be identified in the list (if you are using the panel on the left approach).
By Teddy Jensen - 4/15/2008

In case anyone wants a ToolStripDropDownButton instead of a windowslist in a sidepanel or on a menu you can use this approach.

On the toolstrip i have a button called tsbVinduer so my code on the main form is

Private Sub tsbVinduer_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs) Handles tsbVinduer.DropDownOpening

 

        tsbVinduer.DropDownItems.Clear()

 

        For Each f As Form In Me.MdiChildren

 

            Dim item As ToolStripMenuItem = New ToolStripMenuItem(f.Text)

            item.Tag = f

 

            AddHandler item.Click, AddressOf item_Click

 

            tsbVinduer.DropDownItems.Add(item)

        Next

 

    End Sub

 

    Private Sub item_Click(ByVal sender As Object, ByVal e As EventArgs)

 

        Dim item As ToolStripItem = TryCast(sender, ToolStripItem)

        If item IsNot Nothing Then

            Dim f As Form = TryCast(item.Tag, Form)

            If f IsNot Nothing Then

                f.Activate()

            End If

        End If

    End Sub

It gives me this result:

/Teddy

By Edhy Rijo - 4/16/2008

Hi Teddy,

Thanks for sharing this approach, it definitely provide ideas on how to get similar results in different ways which is very good, specially when learning .NET.

By Paul Chase - 4/16/2008

Just to add another option to this thread you can also handle the Mdi Client Control Added and Removed on your Main MDI Form.

#Region " MDI Client Handled Events"

Protected Sub MDIClient_ControlAdded(ByVal sender As Object, ByVal e As Windows.Forms.ControlEventArgs)

'--> Cast To My Base Class so we get custom properties

Dim loChild As Payroll.Base.WindowsForms.DevExMaintFormBase = CType(e.Control, System.Windows.Forms.Form)

' do anything you need to do -add handlers update custom lists etc

End Sub

Protected Sub MDIClient_ControlRemoved(ByVal sender As Object, ByVal e As Windows.Forms.ControlEventArgs)

'--> Cast To Base Class so we get custom properties

Dim loChild As Payroll.Base.WindowsForms.DevExMaintFormBase = CType(e.Control, System.Windows.Forms.Form)

' do anything you need to do - remove handlers update custom lists etc

End Sub

#End Region

By Keith Gordijn - 10/27/2008

Trent L. Taylor (06/14/2007)
I think that the best thing to do would be to expose the MDI client...or....if your main form just has the MDIContainer property set you can reference it directly. When the form is launched it will have a reference to the parent form in the ParentForm property which you can type and reference. You may need to make your LauncForm method public or Friend so it can be accessed though.



DirectCast(Me.ParentForm, MainForm).LaunchForm(GetType(MyChildForm))




If you are using an MDIClient control and manually adding it to the form, then you can just expose it through a public property and basically do the same thing:



DirectCast(Me.ParentForm, MainForm).MDIClientProperty.LaunchForm(GetType(MyChildForm))




Hi Trent,



My project follows the ideas outlined in this topic by following the StrataFlix example and I would also like to use the LaunchDialog method of the mainform from a "Setup and Configuration" form (strangely also very similar to the PracticeStudio.NET screen shot earlier in this topic) to open a maintenance form as a child form in the main mdi form.



Unfortunately my still limited knowledge of C# wont let me alter the above VB code to a C# version so any help on doing this would be great.



Cheers,

Keith
By Keith Gordijn - 10/27/2008

Hooray, I've had some success! Maybe I should get up at 2am more often. BigGrin



So, now my question is, is this the best way of doing this?



private void tlmSecurity_ItemClicked(object sender, ThemedLinkMenuClickEventArgs e)

{

if (e.Item.Key.Equals("SecurityEditor", StringComparison.OrdinalIgnoreCase))

{

MainForm loForm;

loForm = (MainForm)this.ParentForm;



loForm.LaunchDialog(typeof(SecurityDialog));

}

}




Cheers,

Keith
By Trent L. Taylor - 10/28/2008

That will work fine Smile
By lastcanary - 6/25/2010

Hi,

I would like to use a tabbed form interface similar to the practice studio. Is this a control contained in Strataframe? Could you please help me to create a similar one?

By Ivan George Borges - 6/27/2010

Hiya.

Nope, this is not a StrataFrame control. This is deeply and specificaly built into PracticeStudio. Iwill see if I can get some hints on how to accomplish this and pass them to you.

By Russell Scott Brown - 6/27/2010

Thanks. I would be interested in this too.