Dynamically-created menus and mdi children

S

Steve_Black

I posted a similar message earlier but we've geared away from the
original topic somewhat so I thought I'd post again in hopes of getting
input from others.

I am creating a MenuStrip dynamically. For each ToolStripMenuItem, I'm
adding an event handler so that the same routine gets called regardless
of which menu item was clicked. All of this works great - my menu is
being loaded properly and my routine gets called properly.

Within the Tag property of each ToolStripMenuItem I store the name of a
form that I want opened as an mdi child of my main form. I couldn't
get this to work properly, so for testing purposes I've reduced my
problem to the following scenario (which really has nothing to do with
the Tag property or the fact that my form names are stored in
variables):

Behind my main form I created the following routine:

Private Sub LoadForm()
Dim frm As New frmTest
With frm
.MdiParent = Me
.Left = 0
.Top = 0
.Show()
End With
End Sub

I also added a button to my form and in the click event of the button,
I call the above routine.

In my routine that gets called when a menu item is clicked, I call the
same LoadForm routine.

If I click my button, frmTest loads as an mdi child as it should.
If I click a menu item, my form does not get loaded (and yes, I've
confirmed that the routine is getting called).
Incidentally, if I comment out the line that says '.MdiParent = Me', my
form will open as a normal Windows form. However, as soon as I
uncomment out that line, my form will not load.

I'm puzzled as to why such a simple thing would work when I click a
button but not when I click a menu item.

As an aside, I tried loading a menu with static menu items (instead of
loading it dynamically), and everything worked fine - my mdi child form
loaded properly.

Any help would be greatly appreciated.

Thanks,

Steve
 
T

tommaso.gastaldi

hi Steve,

show the piece of code where you hook the handlers to the menuitems.
Is it done within the MDIParent Load sub?

-tom
 
S

Steve_Black

Hi Tom,

The following two routines are used to load my main menu. The first
one is for my "main" items, and the second one gets called recursively
to load all subitems. The first routine gets called during the load of
my main form.

Public Sub LoadMainMenu(ByVal prmMenu As MenuStrip)
Dim con As SqlConnection
Dim cmd As SqlCommand
Dim drd As SqlDataReader
Dim strSQL As String

prmMenu.Items.Clear()

con = New SqlConnection(THAGlobal.ConnectString)
If IsECCI() Then
strSQL = "SELECT MenuItemID, MenuItemName,
MenuItemFormName, MenuItemFunctionName, MenuItemECCIOnlyFlag FROM
MenuItems WHERE (MenuItemDisplayFlag=1) And (SubMenuOf Is Null) ORDER
BY MenuItemDisplayOrder"
Else
strSQL = "SELECT MenuItemID, MenuItemName,
MenuItemFormName, MenuItemFunctionName, MenuItemECCIOnlyFlag FROM
MenuItems WHERE (MenuItemDisplayFlag=1) And (SubMenuOf Is Null) ORDER
BY MenuItemDisplayOrder"
End If
cmd = New SqlCommand(strSQL)
cmd.Connection = con
Try
con.Open()
Catch ex As Exception
Exit Sub
End Try
drd = cmd.ExecuteReader

Try
Do While drd.Read
Dim tsi As New ToolStripMenuItem

With tsi
.Name = drd("MenuItemID")
.Text = drd("MenuItemName")
.Tag = vbNullString
If drd("MenuItemFormName").ToString <> vbNullString
Then
.Tag = "F_" & drd("MenuItemFormName").ToString
ElseIf drd("MenuItemFunctionName").ToString <>
vbNullString Then
.Tag = "U_" &
drd("MenuItemFunctionName").ToString
End If
If drd("MenuItemECCIOnlyFlag") = 1 Then
.ForeColor = Color.Red
End If
End With
If tsi.Tag <> vbNullString Then
AddHandler tsi.Click, AddressOf
frmMain.MainMenuClicked
End If
Call LoadMenuItems(tsi)
prmMenu.Items.Add(tsi)
Loop
drd.Close()

Catch ex As Exception
Finally
con.Close()
End Try
End Sub

Private Sub LoadMenuItems(ByVal prmParent As ToolStripMenuItem)
Dim con As SqlConnection
Dim cmd As SqlCommand
Dim drd As SqlDataReader
Dim strSQL As String

con = New SqlConnection(THAGlobal.ConnectString)
If IsECCI() Then
strSQL = "SELECT MenuItemID, MenuItemName,
MenuItemFormName, MenuItemFunctionName, MenuItemECCIOnlyFlag FROM
MenuItems WHERE (MenuItemDisplayFlag=1) And (SubMenuOf = " &
prmParent.Name & ") ORDER BY MenuItemDisplayOrder"
Else
strSQL = "SELECT MenuItemID, MenuItemName,
MenuItemFormName, MenuItemFunctionName, MenuItemECCIOnlyFlag FROM
MenuItems WHERE (MenuItemDisplayFlag=1) And (SubMenuOf = " &
prmParent.Name & ") And (MenuItemECCIOnlyFlag = 0) ORDER BY
MenuItemDisplayOrder"
End If
cmd = New SqlCommand(strSQL)
cmd.Connection = con
Try
con.Open()
Catch ex As Exception
Exit Sub
End Try

drd = cmd.ExecuteReader

Try
Do While drd.Read
Dim tsi As New ToolStripMenuItem
With tsi
.Name = drd("MenuItemID")
.Text = drd("MenuItemName")
If drd("MenuItemFormName").ToString <> vbNullString
Then
.Tag = "F_" & drd("MenuItemFormName").ToString
ElseIf drd("MenuItemFunctionName").ToString <>
vbNullString Then
.Tag = "U_" &
drd("MenuItemFunctionName").ToString
End If
If drd("MenuItemECCIOnlyFlag") = 1 Then
.ForeColor = Color.Red
End If
End With
If tsi.Tag <> vbNullString Then
AddHandler tsi.Click, AddressOf
frmMain.MainMenuClicked
End If
Call LoadMenuItems(tsi)
prmParent.DropDownItems.Add(tsi)
Loop
drd.Close()

Catch ex As Exception
Finally
con.Close()
End Try
End Sub

You can see that I added an Event Handler to the Click event of my menu
items (only the relevant ones). The Click event calls the following
routine, which is located in my main form.

Public Sub MainMenuClicked(ByVal sender As System.Object, ByVal e
As System.EventArgs)
Call LoadForm()
End Sub

LoadForm looks like this:

Private Sub LoadForm()
Dim frm As New frmParticipantSearch
With frm
.MdiParent = Me
.Left = 0
.Top = 0
.Show()
End With
End Sub

I also added a button with the following code:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
Call LoadForm()
End Sub

As I said before, if I click my button everthing works fine - my form
loads as an mdi child.

I've confirmed that my MainMenuClicked event is getting called when I
click on any menu item that has a Tag assigned to it, as it should. If
I step through the code at runtime using breakpoints, it'll call
LoadForm and process every line with no errors, but I won't see my
form.

I removed it from here, but I previously added event handlers to the
Load and FormClosed events of my test form just to see if the form was
being loaded and immediately closed, but those events didn't fire (they
were MsgBoxs that did fire when I clicked my button and subsequently
closed my test form).

The thing that gets me most is that if I comment out the line that
makes my test form an mdi child, it works. I just can't assign my test
form as an mdi child if it's being called via the menu.

Also, I added a static menu and behind the click of one of the items I
called LoadForm. Everything worked.

Again, thanks for assistance.

Steve
 
T

tommaso.gastaldi

Let's do a first attempt. Try replacing:

AddHandler tsi.Click, AddressOf frmMain.MainMenuClicked()

with

RemoveHandler tsi.Click, AddressOf frmMain.MainMenuClicked()
AddHandler tsi.Click, AddressOf frmMain.MainMenuClicked()

and let me know if anything changes...

-t
 
T

tommaso.gastaldi

If you have not already done it, try putting a RemoveHandler before
*each* AddHandler, everywhere.
As you describe the problem, it seems a reentrancy problem...

-t

(PS.
Probably it would be easier if you reorganize some parts of code. In my
experience it is usually not advisable to rely on the names of the
controls in the code, as it makes difficult maintenance...)
 
S

Steve_Black

Yes, I put it before every AddHandler, everywhere.

As for your PS, I'm not sure what you mean unless you're referring to
my use of frmMain. Can you elaborate a little (but not so much as to
get away from the real issue here :) )

Thanks again. I'll be checking back in the morning. I'm done for the
night.
 
T

tommaso.gastaldi

hi Steve,

I pasted you code in my editor connecting to an mdb I made on the fly.
Actually it seems to be working. The child forms get open.

One thing I am not clear. How come in :

SubMenuOf = " & prmParent.Name & ") ORDER BY

you do not need quotes as in:

SubMenuOf = """ & prmParent.Name & """) ORDER BY

did they get lost in the paste?

-tom
 
S

Steve_Black

Actually, the Name of the Parent is an integer, as is the value of the
SubMenuOf field. I thought about changing this but haven't done so
yet.

I'm going to try creating a new application and pasting my code into
it. Maybe something is just screwy with my app.

I'll let you know what I find out.

Thanks.
 
T

tommaso.gastaldi

Good. I am looking forward to knowing...

(PS: a recommendation: when you create the new app do enforce strict ON)
 
S

Steve_Black

Okay, I created the new app and it worked. I then figured out what the
differences were and figured out what caused it to not work in my
original app, although I'm still not really sure why.

In my first app, my main form is not the first form that opens. I open
a Login form first and authenticate the user. If the user enters a
valid username/password, I open the Main Form and close the Login form.
In my project properties under the 'Application' tab I had changed my
Startup Form to be my Login form and in the 'Shutdown mode' option I
changed the default (When startup form closes) to 'When last form
closes'.

That seems to be what caused the problem because I am able to duplicate
the problem in my second application.

My second application loads my main form first, and duing the load of
that form I open the Login form with ShowDialog. If the user
authenticates properly I pass back to the Main Form a result of
DialogResult.Yes - otherwise I pass back DialogBox.No. If the user
does not authenticate properly (after 3 tries) I just close the main
form and I'm done. Otherwise my main form continues to load and I load
my main menu. This works fine.

If you've got any idea as to why my first way doesn't load the mdi
children properly, I'd be curious to know. I suspect my second
approach is actually the better approach anyway, so I'm going to stick
with it.

Thanks a lot for the help.

By the way, I noticed that if I open my mdi child it gets places at
Left=0, Top=0 as I specified in my code. However, if I then close my
child form and re-open it, it seems to be positioned lower and to the
right, as if the first form were still opened and the two forms were
being cascaded. My guess is that this is something fairly easy to fix
and I haven't actually looked around for an answer yet, but I thought
I'd ask in the hopes that you know offhand.

Thanks again,

Steve
 
T

tommaso.gastaldi

glad to hear that :)

-tommaso

Private Sub LoadForm()
Dim frm As New frmParticipantSearch
With frm
.MdiParent = Me
.StartPosition = FormStartPosition.Manual '<--
.Location = New Point(0, 0)
'.Left = 0
'.Top = 0
.Show()
End With
End Sub
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top