Link to home
Start Free TrialLog in
Avatar of BlakeMcKenna
BlakeMcKennaFlag for United States of America

asked on

Making MenuStrip Items visible without using a Loop?

I think I found a way to control the menu options visibility in my MDI Form menu but not quite sure how to implement it.

I'm basically storing a key value in the database for each menu item. I use one loop in which to retrieve these values and then do something like this:

   Dim blnFound As Boolean = False

   blnFound = mnu.Items.ContainsKey(mnu_Key)

   If blnFound Then
        Not sure how to take this menu item and make it visible
   End If

Open in new window


Not sure how to make that menu item that is found visible using just the mnu variable (which represents my main menu).
Avatar of Bob Learned
Bob Learned
Flag of United States of America image

Are you using a for loop to create the menu, or are they statically defined in the designer?
From:
http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripitemcollection(v=vs.110).aspx

Note that my VB is a little off . . . it's been forever so it may have syntax errors.  Essentially you want to get the array of items and loop over the array turning each item visible to false.

'get the array of items that have the key.  It's possible to have the same key for multiple items.
Dim mnuItems as ToolStripItemCollection() = mnu.Items.Find(mnu_Key)

'if there are no items this lopp is skipped.
For Each item as ToolStripItem in mnuItems
  item.Visible = false
next

Open in new window

Avatar of BlakeMcKenna

ASKER

Kyle,

Your VB is a little off but that's ok. I've never used a ToolStripItemCollection before. I got the following error in the declaration (see screenshot).

Thanks!
Screenshot.jpg
LearnedOne,

My menu is statically created in the designer...
Perhaps I should explain the whole concept of what I'm trying to do.

My menu structure for my application is already defined within the Designer. The visible property is set to False for each menu item, including the 1st level items.

Each user has their own set of permissions which are defined in a SQL Server DB. I have attached an image of the recordset for a specific user. The definitions of the columns are as follows:

menuName -  contains the value of the "Name" property of the menu item
functionality - contains the menu item literal value
lvlUpdate -      a boolean flag indicating whether the user has update privileges for that
                       menu item functionality
lvlView -          a boolean flag indicating whether the user has viewing privileges for that
                       menu item functionality
userID -          the ID of the User that is logged into the application
functionKey -  a literal value that is added to the StatusStrip
hasBar -          a boolean value indicating if a ToolStripSeparatorBar should be shown after
                       the previous menu item.


I basically read the DB Table and based on whatever records are in the dataset, I need to set the "Visible" property for that specific menu item to true. I'm just not sure of the best way to do this. I know there are several different "ToolStrip*" objects to use.

I hope I've explained this properly. If anything is unclear, please let me know.

Thanks!
Screenshot.jpg
in this case you would loop over the record set:

'assuming you know how to setup the cmd, connection params.
Dim dr as SqlDataReader = cmd.ExecuteReader()
Dim item as ToolStripItem

While (dr.Read())
     'you can access the item by name
     item = mnu.Items(dr("menuName").ToString())
 
     'check for existance
      if item is not nothing then
      'true? -> set visible
       item.visible = true
     else
         'doesn't exist, show error.
       MsgBox ("Could not find menu item: " + dr("menuName").ToString()
     end if
wend
Ok,

Here is what I have so far and it works up to a point.

                    Dim mnuItem As ToolStripItem

                    For Each row As DataRow In EH.DataTable.Rows
                        strMenuName = "" & row(0)
                        lvlView = "" & CInt(row(3))
                        strFKey = "" & row(5)

                        If strFKey.Length > 0 Then
                            gFKeys &= strFKey & "  ¦  "
                        End If

                        mnuItem = mnu.Items(strMenuName)

                        If lvlView <> 0 Then
                            mnuItem.Visible = True     'Receive Error Here
                        End If
                    Next

Open in new window


This logic works up until the menu item is a DropDownItem. When the mnuItem is a DropDownItem, I get that "Object reference not set to an instance of an object" error.
Just a note,  NEVER use row(<int>).  Always call the column by name.  As soon as someone changes the query, (eg: says I want this primary key field up front or something) it'll break the code.  Referencing it by name almost guarantees it will never break.  (I say almost cause someone can change the column name, but then that's on them).

Can you post your menu design?
That makes sense...thanks for the tip! I have attached several screenshots of the menu structure. BTW, is there a way to expand the entire menu structure so a snapshot can be taken?
screen-File-SubMenu.jpg
screen-Calibration-SubMenu.jpg
screen-Setups-SubMenu.jpg
screen-Help-SubMenu.jpg
Unfortunately not.  Just noticed you're checking on the view permission, not on the menu item itself:


                         If lvlView <> 0 AndAlso mnuItem is Not Nothing Then
                            mnuItem.Visible = True     'Receive Error Here
                        End If
It's still not working. What I discovered is that if the database menu item is a DropDownItem rather than a Menu Item, it won't load the menu item name into the mnuItem variable consequently making it equal to "nothing".

      mnuItem = mnu.Items(strMenuName)

Open in new window


mnu is the MenuStrip control being passed into the SubRoutine.
This is what I have so far. I'm trying different things but to no avail.

                    For Each row As DataRow In EH.DataTable.Rows
                        Dim mnuItem(0) As ToolStripItem

                        strMenuName = "" & row("menuName")
                        lvlView = "" & CInt(row("lvlView"))
                        strFKey = "" & row("functionKey")
                        hasBar = "" & row("hasBar")

                        If strFKey.Length > 0 Then
                            gFKeys &= strFKey & "  ¦  "
                        End If

                        mnuItem = mnu.Items.Find(strMenuName, True)

                        If lvlView <> 0 AndAlso Not mnuItem Is Nothing Then
                            If mnuItem(0).Visible = True Then         'INDEX OUT OF BOUNDS ERROR
                                mnuItem(0).Visible = False
                            Else
                                mnuItem(0).Visible = True
                            End If
                        End If
                    Next

Open in new window

Using the above code, I keep getting an "Index was outside the bounds of the array" error on the indicated line. It happens on the 2nd iteration of the For Loop.
For Each row As DataRow In EH.DataTable.Rows
                        Dim mnuItems As ToolStripItem()

                        strMenuName = "" & row("menuName")
                        lvlView = "" & CInt(row("lvlView"))
                        strFKey = "" & row("functionKey")
                        hasBar = "" & row("hasBar")

                        If strFKey.Length > 0 Then
                            gFKeys &= strFKey & "  ¦  "
                        End If

                        mnuItems = mnu.Items.Find(strMenuName, True)

                        'if you have permissions
                        If lvlView <> 0 Then
                       
                         'loop through each menu item and set it to true.  No need to check
                          for each item as ToolStripItem in mnuItems 
                                   item.Visible = True
                          next
                        Else
                            'otherwise, loop through each item with the key and set it to false.
                          for each item as ToolStripItem in mnuItems 
                                item.Visible = False
                          next
                        End If


                    Next 

Open in new window

Kyle have you tested this? I only ask because it still only shows the 1st level of menu items and not the 2nd level. Here is what I have!

                    For Each row As DataRow In EH.DataTable.Rows
                        Dim mnuItem As ToolStripItem()

                        strMenuName = "" & row("menuName")
                        lvlView = "" & CInt(row("lvlView"))
                        strFKey = "" & row("functionKey")
                        hasBar = "" & row("hasBar")

                        If strFKey.Length > 0 Then
                            gFKeys &= strFKey & "  ¦  "
                        End If

                        mnuItem = mnu.Items.Find(strMenuName, True)

                        If lvlView <> 0 Then
                            For Each item As ToolStripItem In mnuItem
                                item.Visible = True
                            Next
                        Else
                            For Each item As ToolStripItem In mnuItem
                                item.Visible = False
                            Next
                        End If
                    Next

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Kyle Abrahams, PMP
Kyle Abrahams, PMP
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Kyle,

Your code works but it goes back to what I had successfully used before and that was using several For Loops. I'll just have to trade a little performance for functionality.

Your code does actually eliminate 1 For Loop so it's an improvement over my code.

Thanks for your help!
It looks like a necessary evil in this case.

If you're not controlling sub items, you could get away with leaving them visible and only turning on the parent.  (The theory being if the parent is hidden the children should be as well).

If you are hiding sub elements then you need:

- A for to process your database
- A for loop for every item (and subitems of items recursively)

Right now you only have 2 levels but you can have more than that.  This code should handle N levels.

Best of luck, glad we got it working.