[RESOLVED] Creating menu items at runtime
Hi all. I have a context menu that which I want to display the index of each item from a listbox as menu items in a context menu. For example, if there are 5 items in the listbox, I would like the context menu to be as follows:
Stop after track... -->
1
2
3
4
5
I would also like the menu items to be called "mnuStopTrack" and then the number that is displayed i.e. "mnuStopTrack3." How would I do this using a loop? I then need to be able to set properties if the dynamically made menu item is clicked.
Re: Creating menu items at runtime
Your menu items can't be "called" anything. When you create a MenuItem at design time the IDE creates a member variable in the form and you then use that variable to access the MenuItem. When you create a MenuItem at run time you can't do that. You can assign a reference to the MenuItem to any member variable that already exists, like a MenuItem variable, an element of an array or an item of a collection, but you can't create new member variables at run time.
Your ContextMenu object has a MenuItems property. At run time you can create new MenuItem objects and Add them to that collection. The MenuItem constructor is overloaded and several overloads accept an EventHandler that will be used to handle the Click event. I suggest that you use the same procedure to handle the Click event for all items, then test the Text property of the sender and determine what to do from that. Take a look at the help topic for the MenuItem constructor and you'll get get the idea.
Re: Creating menu items at runtime
Thanks for the reply. I have created the menu items, but I am having some trouble creating some very simple code for the click event of each menu item. Also, in what event would I create these items if I want them to be the child items of another item called 'mnuStopTrack'? I need the list to be populated when 'mnuStopTrack' is drawn (I tried doing my code in the drawitem event but that didn't work) because I need it to have a little arrow next to it that will allow the user to see the dynamically populated menu items. That probably made no sense...
Re: Creating menu items at runtime
I would say that the best place to do this would be in the handler for the Popup event of the ContextMenu. It looks to me like you have a number of Integers in your ListBox. If you want to create a MenuItem that corresponds to each of those values then I'd suggest something like this:
VB Code:
Private Sub ContextMenu1_Popup(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ContextMenu1.Popup
'Destroy the existing menu items.
For Each mi As MenuItem In Me.mnuStopTrack.MenuItems
mi.Dispose()
Next mi
'Clear the parent menu's child collection.
Me.mnuStopTrack.MenuItems.Clear()
Dim menuItems(Me.ListBox1.Items.Count - 1) As MenuItem
'Create the new menu items.
For i As Integer = 0 To Me.ListBox1.Items.Count - 1 Step 1
menuItems(i) = New MenuItem(Me.ListBox1.GetItemText(Me.ListBox1.Items(i)), New EventHandler(AddressOf SetTrackStopMark))
Next i
'Add the child items to the parent.
Me.mnuStopTrack.MenuItems.AddRange(menuItems)
End Sub
Private Sub SetTrackStopMark(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim stopMark As Integer = Convert.ToInt32(DirectCast(sender, MenuItem).Text)
'Use the stopMark variable as required here.
End Sub
Re: Creating menu items at runtime
That's exactly what I needed THANKS!! However, I am still confused how I would use the stopMark variable. All I would like to do is set this public variable called 'stopTrack' equal to a number that is in the listbox item. For example: '1. beethoven - 9th symphony.' I know how to extract the number, but where would i set 'stopTrack' equal to the selected dynamic menu item? Also, how would i set the new menu items as checked if clicked? I forgot to to tell you that my items are not necessarily numbers. I am using the following code to extract the number from the track information. However, how would I find out which menu item has been clicked on?
VB Code:
Private Sub SetTrackStopMark(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim SpaceIndex As Integer
SpaceIndex = HERE IS WHERE I NEED TO KNOW WHAT ITEM WAS CLICKED.IndexOf(".")
mnuNumber = HERE IS WHAT I NEED TO KNOW WHAT ITEM WAS CLICKED.Remove(0, SpaceIndex + 1)
Dim stopMark As Integer = Convert.ToInt32(mnuNumber)
'Use the stopMark variable as required here.
End Sub
Sorry that I am stupid :D
Re: Creating menu items at runtime
Everthing's easy when you know how and hard when you don't. :) The sender argument in an event handler is ALWAYS the object that raised the event, so all you have to do is cast the sender as a MenuItem and set its Checked property. Note that my code refreshes the menu items every time the ContextMenu is displayed, so you'll either have to change that or remember which item is checked before you clear the list and then reset it after repopulating it.
I've reread your posts and I now realise that you want the indices of the ListBox items on your menu rather than the text of each item. For that you would change this line:
VB Code:
menuItems(i) = New MenuItem(Me.ListBox1.GetItemText(Me.ListBox1.Items(i)), New EventHandler(AddressOf SetTrackStopMark))
to this:
VB Code:
menuItems(i) = New MenuItem((i + 1).ToString(), New EventHandler(AddressOf SetTrackStopMark))
Then you could change the event handler to look something like this:
VB Code:
Private Sub SetTrackStopMark(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim stopMark As Integer = Convert.ToInt32(DirectCast(sender, MenuItem).Text) - 1
Me.stopTrack = Me.ListBox1.Items(stopMark)
End Sub
Re: Creating menu items at runtime
Hi. I edited my previous post right before you posted. Read the last bit. Basically, my context menu will actually not be just numbers (that is a recent change I have made).
Re: Creating menu items at runtime
Fixed it:
VB Code:
Private Sub SetTrackStopMark(ByVal sender As System.Object, ByVal e As System.EventArgs)
Dim SpaceIndex As Integer
Dim mnuText As String
Dim mnuNumber As Integer
mnuText = DirectCast(sender, MenuItem).Text
SpaceIndex = mnuText.IndexOf(".")
mnuNumber = mnuText.Remove(SpaceIndex, mnuText.Length - SpaceIndex)
Dim stopMark As Integer = Convert.ToInt32(mnuNumber)
'Use the stopMark variable as required here.
MsgBox(stopMark)
End Sub
Thanks for your help!
Re: [RESOLVED] Creating menu items at runtime
Again, sender argument of an event handler is always the object that raised the event, so if your menu item text looks something like "blahblahblah.number" and you want just the number then you can do this:
VB Code:
Dim itemText As String = DirectCast(sender, MenuItem).Text
Dim number As Integer = itemText.Substring(itemText.LastIndexOf("."c) + 1)
Note that I used LastIndexOf just in case there happens to be a dot in the track name somewhere.
Edit:
You posted while I was typing. :)
Re: [RESOLVED] Creating menu items at runtime
Looks like it's "number.blahblahblah" so I'd change:
VB Code:
Substring(itemText.LastIndexOf("."c) + 1)
to:
VB Code:
Substring(0, itemText.IndexOf("."c))
Re: Creating menu items at runtime
First of all, doesn't 'indexof' return the first instance of whatever it is you are looking for? Also, I ran into a little problem... How would I keep a runtime-created menu item checked if we clear them all out every time the context menu pops up??
Re: Creating menu items at runtime
Re: Creating menu items at runtime
Yes, IndexOf does return the first occurrence, but the your code in post #5 Removes characters from the front of the string, indicating you wanted to keep the characters from the end, in which case LastIndexOf would have been appropriate. You've changed your code to Remove characters from the end so you'll note that my most recent post has adjusted my code accordingly, including switching LastIndexOf to IndexOf. As for keeping the item checked I've already addressed that.
Quote:
Originally Posted by jmcilhinney
Note that my code refreshes the menu items every time the ContextMenu is displayed, so you'll either have to change that or remember which item is checked before you clear the list and then reset it after repopulating it.
It seems odd that the menu gets refreshed each time, but that's what you said you wanted so that's what I provided. I'd think that it would be best to populate the menu as infrequently as possible, like only when the items will have changed.