Home All Groups Group Topic Archive Search About

Dynamic Menu-building problem

Author
13 May 2007 8:00 PM
Andy
This is really a continuation of an earlier thread - see
http://msdn.microsoft.com/newsgroups/default.aspx?dg=microsoft.public.vb.general.discussion&mid=d6275694-54f8-4542-88e3-b7aaf772295f

The last exchange on the old thread was on April 24, so perhaps it's not
going to show up on folks' browsers any more.  I had a few exchanges with
Steve Gerrard, and a lot of my problems were eventually solved after those,
but we concentrated on the mechanics of containing one UC inside another, and
my original problem (see subject) has still not been solved.  Steve, I hope
you're still watching!

To recap - I have a form which displays one of many user controls, the UCs
being sited on the form dynamically when it is opened, and according to what
it is intended to do.  Other controls on the form allow the user to switch
between visible UCs, or to move the current UC across a host PictureBox using
scrollbars, in the event that the form has been resized to the point that the
entire UC is not visible within the host PictureBox.  Previously the app had
many different forms, and the selection of UCs on each was fixed, but the
resizing management code was identical across them all, and I wanted to avoid
having to maintain it in many places.  Hence the move to a single form and
dynamic siting of UCs.

OK - in the days when I had many forms, the various menus were all written
into the forms, and if a different UC were selected by the user, the form
could hide some menus and display others.  With my new scenario, I have menus
written into the UCs, so that if a new one gets the focus, its menus appear
instead of those of the previous UC.

However - I mentioned a few moments ago that the form has other controls on
it.  If one of those gets the focus, the UC's menus disappear.  That's not
what I want to happen.  I want the presently visible UC's menus showing on
the form even when it loses the focus.  After all, it seems unreasonable to
expect the user to click on the UC to get its menus back!  I have one simple
option - the code can immediately set the focus back to the UC as soon as one
of the other controls gets it.  But this isn't pretty - because the UC's
menus momentarily disappear and then reappear.  In fact, it looks pretty ugly.

I think the only way to avoid this is to make the menus part of the form,
and set them up dynamically at the same time that the form is configured with
its selection of UCs.  But I have no idea how to do this.  Perhaps someone
here could explain?

Since I will have to manage the showing and hiding of menus myself, I am
going to need some way of reproducing (a) menu(s) without going through the
form configuration code again - so I need to store the definition of each
menu somehow, so that it can be rebuilt as required.  How will I associate a
menu item with a specific code entry point in the UC?  What happens if some
condition in the UC requires that a menu item become invisible, disabled, or
has a check mark added (or removed)?  Clearly the UC needs some way to
control its menus, even though they are now owned by the form.

Author
13 May 2007 9:43 PM
Steve Gerrard
"Andy" <andrewjellis@hotmailnospam.com> wrote in message
news:8D47B357-AEC7-4590-9B3B-D3D413C96C46@microsoft.com...
> This is really a continuation of an earlier thread - see
> http://msdn.microsoft.com/newsgroups/default.aspx?dg=microsoft.public.vb.general.discussion&mid=d6275694-54f8-4542-88e3-b7aaf772295f
>
>  Steve, I hope you're still watching!

Yup I am, not sure if that is good :)


> I think the only way to avoid this is to make the menus part of the form,
> and set them up dynamically at the same time that the form is configured with
> its selection of UCs.  But I have no idea how to do this.  Perhaps someone
> here could explain?
>
> Since I will have to manage the showing and hiding of menus myself, I am
> going to need some way of reproducing (a) menu(s) without going through the
> form configuration code again - so I need to store the definition of each
> menu somehow, so that it can be rebuilt as required.  How will I associate a
> menu item with a specific code entry point in the UC?  What happens if some
> condition in the UC requires that a menu item become invisible, disabled, or
> has a check mark added (or removed)?  Clearly the UC needs some way to
> control its menus, even though they are now owned by the form.

It seems to me you have two options. The first is to put all the menus for all
the UCs in the main form, then show and hide them when the form changes to a
different UC. That would separate the menus from the UCs, which is not so good,
but you could use the menu designer to build them all.

The second is to setup a class that defines the properties a menu needs
(caption, etc), then have each UC expose a collection of these CMenu items. If
you need nesting, you would need to build that into the class as well,
presumably as a collection of NestedItems, which are themselves CMenus. Try to
minimize nesting, as loading them dynamically will be trickier.

In either case, you should become familiar with menu arrays, where the items
have the same name and different indexes. As with control arrays, these all fire
the same event, which receives the Index as a parameter. You will need some base
menu items on the main form, setup to be menu arrays, which you can then fill in
dynamically.

On the main form, the event handler for a menu item can call the current UC,
telling it which menu item got clicked. You could use the Tag property of menu
items to help identify them, or use the indexes or captions. The UC would have a
method such as MenuClicked that can be called from the main form. It would then
do some sort of Select Case on the passed parameter, and act accordingly.

You could also raise an event from the UC when a menu state needs to change. It
would need to identify which menu item to change, and what to change. The main
form can then receive the event and make the change. Or the main form can ask
the UC about state just before showing the menu items, when the top level menu
gets clicked.

Nobody said it would be simple...
Author
14 May 2007 2:36 AM
Andy
"Steve Gerrard" wrote:

> >  Steve, I hope you're still watching!
>
> Yup I am, not sure if that is good :)
It's good for me - at least you have a fair idea where I'm at with this. 
Although I can see I might have to explain the app in a bit more detail for
you to see where I'm going with it...

> It seems to me you have two options. The first is to put all the menus for all
> the UCs in the main form, then show and hide them when the form changes to a
> different UC. That would separate the menus from the UCs, which is not so good,
> but you could use the menu designer to build them all.
I'm not sure I like that idea very much.  I could end up with very many menu
items on the form, and a huge 'ConfigureForm' Sub which serves to hide most
of them, depending on which object type will be maintained by a particular
form instance.  I still have the problem of specifying which UC's code (and
which public Sub within that UC) must be called when each menu item is
clicked.  That kind of suggests having a structure within the form which is
maintained by Sub ConfigureForm, and by means of which the corresponding Sub
in a UC is called in response to clicking a menu item.  That could be messy.

> The second is to setup a class that defines the properties a menu needs
> (caption, etc), then have each UC expose a collection of these CMenu items. If
> you need nesting, you would need to build that into the class as well,
> presumably as a collection of NestedItems, which are themselves CMenus. Try to
> minimize nesting, as loading them dynamically will be trickier.
Maybe I like this approach a bit more.  I'm not too worried about nesting,
because collections don't frighten me.  I already have a collection of
'EditMode's in the main form - each EditMode has a reference to the hosted UC
which must be brought into view when the corresponding (leaf) node in the
TreeView is clicked.  (Remember my TreeView?  I now add nodes to this from
ConfigureForm, by calling Subs in the main form.  The nodes in the TreeView
have keys matching those of the EditModes collection, so it's an easy matter
to identify which UC to bring to the front after a TreeView node is clicked.)
If there's a corresponding Menu structure to be stored against an EditMode,
I can add that to the EditMode class, and all I need is a means of rebuilding
the menu from the stored structure.  But against each menu item rebuilt in
this way, I need to make the association with a specific chunk of code in the
UC.

> On the main form, the event handler for a menu item can call the current UC,
> telling it which menu item got clicked. You could use the Tag property of menu
> items to help identify them, or use the indexes or captions. The UC would have a
> method such as MenuClicked that can be called from the main form. It would then
> do some sort of Select Case on the passed parameter, and act accordingly.
OK, I can see how that works.

> You could also raise an event from the UC when a menu state needs to change. It
> would need to identify which menu item to change, and what to change. The main
> form can then receive the event and make the change. Or the main form can ask
> the UC about state just before showing the menu items, when the top level menu
> gets clicked.
That looks like good common sense as well.  Although I can see a major
complication with it.  Remember, my UCs are there because they might be
present on many different instances of the form, and the different instances
may potentially host different selections of UCs.  That means there's always
the danger that between different instances of the form, the specific menu
items could have different Index values.  I will have to keep a lookup
dictionary in the form, and set this up from Sub ConfigureForm, so that the
form can identify which menu item to modify by concatenating the current
EditMode's key with the menu item identifier passed in the event raised by
the UC, then looking that up in the dictionary to find the correct menu item
in the form.  Looks like great fun!

Please don't go away - I can see I might have further issues to solve before
I have this working properly.
Author
14 May 2007 3:03 AM
Steve Gerrard
Show quote
"Andy" <andrewjellis@hotmailnospam.com> wrote in message
news:B4664C30-DF3F-49F2-BC1F-2EF08A9A334D@microsoft.com...
> "Steve Gerrard" wrote:
>

>> The second is to setup a class that defines the properties a menu needs
>> (caption, etc), then have each UC expose a collection of these CMenu items.
>> If
>> you need nesting, you would need to build that into the class as well,
>> presumably as a collection of NestedItems, which are themselves CMenus. Try
>> to
>> minimize nesting, as loading them dynamically will be trickier.

> Maybe I like this approach a bit more.

It is more work, but it is a more robust and flexible way to go, especially if
further development will occur in the future.

> Please don't go away - I can see I might have further issues to solve before
> I have this working properly.

LOL. I'm not going anywhere soon, save a couple of days to see my nephew
graduate high school.


If you are going to try this route, I would begin with the CMenuItem class. The
main properties to have are Caption, Tag, Checked, Enabled, and Visible, to be
set in the UC code as it builds its collection. You could also add an Index,
initially 0. If this is read/write, you could store the actual index in it when
it gets put in a menu on the main form. However I would try and use the Tag
property to keep track of the menu items, unless you need it for something else.

If you are going to have sub menus, then CMenuItem would also need a collection
property that would return a collection of CMenuItem objects to define it's sub
menu.

To set them up in your UC's, you can open each .ctl file using Notepad. You will
find the menu described in the first part of the file, before Option Explicit
and the beginning of the actual code. It might look something like

   Begin VB.Menu mnuTest
      Caption         =   "Test"
      Enabled         =   0   'False
      Begin VB.Menu mnuTestA
         Caption         =   "TestA"
         Checked         =   -1  'True
      End
   End
   Begin VB.Menu mnu2
      Caption         =   "Menu2"
      Enabled         =   0   'False
   End

Using this, you can write code to create a collection of CMenuItem objects in
each UC, to match the menu you have put there using the designer.

Once that's all done, we can talk about the code in the main form that will load
up real menu items based on this data.

One hint of what I would recommend, and have done myself in a major app: create
an interface such as UCWithMenu, and implement it in each UC. This will allow
the form code to work with different UCs the same way. More next time...
Author
14 May 2007 3:08 AM
Steve Gerrard
"Steve Gerrard" <mynameh***@comcast.net> wrote in message
news:35adnVl3IJrHTNrbnZ2dnUVZ_sWdnZ2d@comcast.com...
>
> "Andy" <andrewjellis@hotmailnospam.com> wrote in message
> news:B4664C30-DF3F-49F2-BC1F-2EF08A9A334D@microsoft.com...
>> "Steve Gerrard" wrote:
>>
>
>
> Using this, you can write code to create a collection of CMenuItem objects in
> each UC, to match the menu you have put there using the designer.
>

One more thing: if you make the Tag property of each CMenuItem be what was the
Name property of the original menu item, your later code will still make sense,
and require less changes. The main MenuClicked(Tag As String) sub in your UC can
do a select case on the Tag, and then call the matching menu click handler you
already have.
Author
14 May 2007 3:12 AM
Steve Gerrard
"Steve Gerrard" <mynameh***@comcast.net> wrote in message
news:upCdnRx-vZoTT9rbnZ2dnUVZ_sSmnZ2d@comcast.com...
>
>

One more-more thing: if you want to take this out of the newsgroup, feel free to
email instead. Just put my name without spaces where it says to in
mynameh***@comcast.net
Author
13 May 2007 10:38 PM
Steve Gerrard
"Andy" <andrewjellis@hotmailnospam.com> wrote in message
news:8D47B357-AEC7-4590-9B3B-D3D413C96C46@microsoft.com...
> This is really a continuation of an earlier thread - see
> http://msdn.microsoft.com/newsgroups/default.aspx?dg=microsoft.public.vb.general.discussion&mid=d6275694-54f8-4542-88e3-b7aaf772295f
>

Or maybe just use a toolbar on each UC?
Author
14 May 2007 1:43 AM
Andy
"Steve Gerrard" wrote:

> Or maybe just use a toolbar on each UC?

But wouldn't I see the same problem - i.e. the toolbar would disappear when
one of the other controls on the main form got the focus?
Author
14 May 2007 2:35 AM
Steve Gerrard
"Andy" <andrewjellis@hotmailnospam.com> wrote in message
news:4DC80D85-F22D-40E1-A1E3-34E4A25D18A8@microsoft.com...
> "Steve Gerrard" wrote:
>
>> Or maybe just use a toolbar on each UC?
>
> But wouldn't I see the same problem - i.e. the toolbar would disappear when
> one of the other controls on the main form got the focus?

No, the toolbar could be in the UC, and would a part of it, like a picture box,
aligned at the top of it. It doesn't have the same range as a menu, i.e. drop
down items can't be checked, but it can live in the UC.
Author
14 May 2007 2:54 AM
Andy
"Steve Gerrard" wrote:

> No, the toolbar could be in the UC, and would a part of it, like a picture box,
> aligned at the top of it. It doesn't have the same range as a menu, i.e. drop
> down items can't be checked, but it can live in the UC.

I see what you mean - but I have to dismiss that idea I think.  Remember, my
UCs are hosted in a PictureBox (picHost) on the form, and if there isn't
enough room in picHost for the entire UC, we get scrollbars for repositioning
the UC so that (for example) we can now see the bottom of the UC instead of
the top.  That means the toolbar would disappear off the top.  Any 'tools'
functionality provided by the toolbar would then be unavailable.  So the
toolbar would have to be independent of the UC.  With a menu, the
functionality is always available.

AddThis Social Bookmark Button