MenuItem multiple parent - vb.net

I have a menu item that appears when right clicking over a datagridview. From their the user hovers over a menuitem and a list of other menu items appears, once again this repeats. Giving soemthing like this.
---------
| FOO |---------
---------| BAR |------------
---------| FOOBAR |
------------
There is only to be an event (addressof) handled on the the 3rd tier. With that being said I need to grab the parent.name from FOOBAR and the parent.name of the said parent (grandparent).
Here is where I am at:
If currentMouseRow >= 0 AndAlso currentMouseColumn <= 1 Then
dataGridView_monitorMapping.Rows(currentMouseRow).Selected = True
mainMenu.MenuItems.Add(New MenuItem("Set Monitor(s) Settings"))
mainMenu.MenuItems.Add(New MenuItem("Sync Monitor Mapping View", AddressOf triggerSync))
'list avaliable priorites
For Each priorityRow As DataRow In priorityTypesDS.Tables(0).Rows
Dim rowPriortiyName As String = CStr(priorityRow("Priority"))
Dim subMenu_priorities_item As New MenuItem(rowPriortiyName)
mainMenu.MenuItems(0).MenuItems.Add(subMenu_priorities_item)
'list avaliable boards
For Each boardRow As DataRow In serviceBoardDS.Tables(0).Rows
Dim rowBoardName As String = CStr(boardRow("SvcBrd"))
Dim subMenu_boards_item As New MenuItem(rowBoardName)
subMenu_priorities_item.MenuItems.Add(subMenu_boards_item)
'list avaliable types based on board
If rowBoardName IsNot Nothing Then
Dim availableSvcTypes As DataSet = GetServiceTypes(_objhost, serviceTypes, rowBoardName)
For Each svcTypeRow As DataRow In availableSvcTypes.Tables(0).Rows
Dim rowSvcTypeName As String = CStr(svcTypeRow(1))
Dim subMenu_svcType_item As New MenuItem(rowSvcTypeName, AddressOf triggerSync)
subMenu_boards_item.MenuItems.Add(subMenu_svcType_item)
Next
End If
Next
Next
mainMenu.Show(dataGridView_monitorMapping, New Point(e.X, e.Y))
End If
And the event handler
Public Sub updateMultiRowSettingChange(ByVal Sender As System.Object, ByVal e As System.EventArgs)
'TODO | Handle selection from right click menu.
End Sub

Something like this should get you what you are looking for:
Public Sub updateMultiRowSettingChange(ByVal Sender As System.Object, ByVal e As System.EventArgs)
Dim item As MenuItem = CType(sender, MenuItem)
Dim parent As MenuItem = CType(item.Parent, MenuItem)
Dim grandparent As Menu = parent.Parent
End Sub
However, it may be simpler to set the tag on the menu item to whatever information you need to know where it's coming from, then you could just check item.Tag after you cast it to the MenuItem class.

Related

Selecting 1 Tool Strip Menu Item at any one time

I have a Tool strip menu with 4 options.
The options are degrees of screen rotation. (0, 90, 180, 270)
I am trying to select only 1 of these and keep it selected.
When I choose one then another, both are ticked (selected).
I have searched and found some solutions but only this works for me.
I have used the click event for each option to clear the ones not chosen like below.
Private Sub DegreesToolStripMenuItem6_Click(sender As Object, e As EventArgs) Handles DegreesToolStripMenuItem6.Click
DegreesToolStripMenuItem6.Checked = True
DegreesToolStripMenuItem7.Checked = False
DegreesToolStripMenuItem8.Checked = False
DegreesToolStripMenuItem9.Checked = False
End Sub
This sub is in my code 4 times to make it work but i'm sure there must be a cleaner, easier way to do this.
I have found online some other solutions but I can't seem to make it work, this one kind of makes sense like it should work but I can't figure it out
Private Sub DegreesToolStripMenuItem6_CheckedChanged(sender As Object, e As EventArgs) Handles DegreesToolStripMenuItem6.CheckedChanged, _
DegreesToolStripMenuItem7.CheckedChanged, _
DegreesToolStripMenuItem8.CheckedChanged, _
DegreesToolStripMenuItem9.CheckedChanged
Dim currentItem As ToolStripMenuItem = TryCast(sender, ToolStripMenuItem)
If currentItem IsNot Nothing AndAlso currentItem.Checked Then
Dim menu As ContextMenuStrip = TryCast(currentItem.Owner, ContextMenuStrip)
If menu IsNot Nothing Then
'The current item has just been checked so uncheck all other items on the same context menu strip.
For Each item As ToolStripMenuItem In menu.Items
If item IsNot currentItem Then
item.Checked = False
End If
Next item
End If
End If
End Sub
the toolstrip menu reads like this
-PreferedRotationToolStripMenuItem-DegreesToolStripMenuItem6
DegreesToolStripMenuItem7
DegreesToolStripMenuItem8
DegreesToolStripMenuItem9
Thanks for your time and looking forward to any help you guys can give.
Set CheckOnClick to True for each menu item, which I'm guessing you already have. This code worked for me:
Private Sub ToolStripMenuItems_CheckedChanged(sender As Object, e As EventArgs) Handles DegreesToolStripMenuItem9.CheckedChanged,
DegreesToolStripMenuItem8.CheckedChanged,
DegreesToolStripMenuItem7.CheckedChanged,
DegreesToolStripMenuItem6.CheckedChanged
Dim currentMenuItem = DirectCast(sender, ToolStripMenuItem)
If currentMenuItem.Checked Then
Dim menu = DirectCast(currentMenuItem.Owner, ContextMenuStrip)
For Each menuItem In menu.items.Cast(Of ToolStripMenuItem)
menuItem.Checked = menuItem Is currentMenuItem
Next
End If
End Sub
Here's a slight variation that avoids checking the already checked current item:
Private Sub ToolStripMenuItems_CheckedChanged(sender As Object, e As EventArgs) Handles DegreesToolStripMenuItem9.CheckedChanged,
DegreesToolStripMenuItem8.CheckedChanged,
DegreesToolStripMenuItem7.CheckedChanged,
DegreesToolStripMenuItem6.CheckedChanged
Dim currentMenuItem = DirectCast(sender, ToolStripMenuItem)
If currentMenuItem.Checked Then
Dim menu = DirectCast(currentMenuItem.Owner, ContextMenuStrip)
For Each menuItem In menu.Items.Cast(Of ToolStripMenuItem).Except({currentMenuItem})
menuItem.Checked = False
Next
End If
End Sub
The code above works if you have added the items to a ContextMenuStrip. If you have added them directly to an item on a MenuStrip then change this:
Dim menu = DirectCast(currentMenuItem.Owner, ContextMenuStrip)
to this:
Dim menu = DirectCast(currentMenuItem.Owner, ToolStripDropDownMenu)

Update text in dynamically created label

I'm working on a proof of concept type situation that will eventually be tied to a scheduling database. As a Test I created this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'AddButton("test")
addLots()
End Sub
Private Sub AddLots()
Dim x As Integer
For x = 0 To 10
Dim b As New Button
Dim newLabel As New Label
newLabel.Location = New Point(100, x * 20)
newLabel.Name = x
newLabel.BorderStyle = BorderStyle.Fixed3D
newLabel.Text = newLabel.Name
Me.Controls.Add(newLabel)
Me.Controls.Add(b)
b.Location = New Point(20, x * 20)
b.Text = x
b.Tag = x
b.Name = x
AddHandler b.Click, AddressOf Button_Click
Next
End Sub
Private Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim B As Button = sender
MsgBox(B.Name)
End Sub
For this proof of concept, I simply want label 1 text to be updated when I press button 1 seems like a simple process but it's kicking my butt.
As for any object, to affect a Label you will need a reference to it. As it stands, the only reference you have is via the Controls collection of the parent control you added the Label to, i.e. the form itself. You could loop through the Controls of the form and as soon as you find a Label then you know you have the first one, or you could call OfType and First or FirstOrDefault. That assumes that there are no other Label controls on the form.
You might also consider using a dedicated parent control so that you know it will only contain the Label controls you created at run time. The obvious choice would be a TableLayoutPanel because it will handle the layout for you too.
If accessing the dynamic controls via a Controls collection is an issue then keep your own collection. Declare a member variable of type List(Of Label) and add each Label you create to it. You can then access your control from that collection and know that there are no other controls in there to get mixed up with.
By the way, if you're creating those controls at run time then they won't be automatically disposed when the form is. Make sure that you dispose them yourself and also use RemoveHandler for each AddHandler you used.
Actually, looking closer at your code, I just realised that there's a 1:1 correspondence between the Button and Label controls. It would make sense to use that. Two options are to assign the corresponding Label to the Tag of each Button or else use a Dictionary(Of Button, Label) assigned to a member variable to store the relationships. That way, you can then use the sender in the event handler, which will be the Button that was clicked, to get the corresponding Label.
Option 1.
Creating the Label:
Dim btn As New Button
Dim lbl As New Label
btn.Tag = lbl
In the event handler:
Dim btn = DirectCast(sender, Button)
Dim lbl = DirectCast(btn.Tag, Label)
Option 2.
At class level:
Private labelsByButton As New Dictionary(Of Button, Label)
Creating the Label:
Dim btn As New Button
Dim lbl As New Label
Me.labelsByButton.Add(btn, lbl)
In the event handler:
Dim btn = DirectCast(sender, Button)
Dim lbl = Me.labelsByButton(btn)
I added this to the button click event. Doesn't seem very efficient as I will eventually have 30-40 buttons and controls on the form but it works.
Private Sub Button_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim B As Button = sender
Dim lblToChange As Integer = B.Name
For Each objCtrl As Control In Me.Controls
If TypeOf objCtrl Is Label Then
Dim Lbl As Label = DirectCast(objCtrl, Label)
If Lbl.Name = lblToChange Then
Lbl.Text = "This ONe"
End If
End If
Next
End Sub

VB.NET ComboBox results selected item

Hi I have a vb windows form application that has a ComboBox from the form1 I have some code that reads some registry and adds item results to combobox. I would like to select one of the results and run a start process. My problem is where do I put the code when item is selected then do something and how to I determine what has been selected?
My Code to query registry keys
Dim Key, Reader As RegistryKey, Y As String
Key = Registry.LocalMachine.OpenSubKey("SOFTWARE\AppStream\AppMgr\Shortcuts", False)
For Each X In Key.GetSubKeyNames
Reader = Registry.LocalMachine.OpenSubKey("SOFTWARE\AppStream\AppMgr\Shortcuts\" & X, False)
If Reader.GetValueNames().Contains("AppTitle") Then
Y = Reader.GetValue("AppTitle")
If Not ComboBox1.Items.Contains(Y) Then ComboBox1.Items.Add(Y)
End If
If i do somehting like this, it just shows a blank messagebox and I have not selected that text from combobox yet.
If ComboBox1.SelectedText Then
MessageBox.Show(ComboBox1.SelectedText())
End If
You subscribe to the SelectedIndexChanged event writing a method like this
Private Sub ComboBox1_SelectedIndexChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndexChanged
Dim comboBox As comboBox = CType(sender, comboBox)
' Caution, the event could be called also when there is nothing selected
if combBox.SelectedItem IsNot Nothing Then
Dim curValue = CType(combBox.SelectedItem, String)
'do your stuff with the selected key'
End If
End Sub
if combBox.SelectedItem IsNot Nothing Then
Dim cmbselected As String = DirectCast(DirectCast(DirectCast(DirectCast(combBox, System.Windows.Controls.ComboBox).SelectedValue, System.Object), System.Data.DataRowView).Row, System.Data.DataRow).ItemArray(0)
End If

Programmatically setting properties of controls on tab pages of a tabcontrol

I am working with a tabcontrol on which I create page one with the designer. I am creating new tab pages with controls on the pages programmatically. On each page is a several panels, each with two radiobuttons (one yes,another no). There is a panel nested inside the first panel with its visible property set to false. If the user selects yes, I want the nested panel's visible property set to true which will reveal several more radiobuttons from which they must make more choices.
My problem is in changing the nested panel's property on any page other than page one.. I can detect the radiobuttons, but I can't seem to find a way to make the nested panel visible.
Public Class ControlProgram
Dim pageindx as integer
Private Sub btnAddPrgm1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddPrgm1.Click
Dim newTab As New TabPage()
pageindx = (TabControl1.TabPages.Count + 1)
newTab.Text = "Step " & pageindx
'define fill panel controls
Dim newpnlFill As New Panel
Dim newlblFill As New Label
Dim newFillY As New RadioButton
AddHandler newFillY.CheckedChanged, AddressOf CheckforCheckedChanged
Dim newFillN As New RadioButton
AddHandler newFillN.CheckedChanged, AddressOf CheckforCheckedChanged
'add fill panel controls
With newTab.Controls
.Add(newpnlFill)
With newpnlFill
.Location = New System.Drawing.Point(6, 6)
.Size = New System.Drawing.Size(171, 137)
.BorderStyle = BorderStyle.FixedSingle
.Controls.Add(newlblFill)
With newlblFill
.Name = "Fill"
.Text = "Fill ?"
.Font = New Font(newlblFill.Font, FontStyle.Bold)
.Location = New Drawing.Point(5, 3)
End With
.Controls.Add(newFillY)
With newFillY
.Name = "FillY"
.Text = "Yes"
.Location = New Drawing.Point(23, 28)
.Size = New System.Drawing.Size(43, 17)
End With
.Controls.Add(newFillN)
With newFillN
.Name = "FillN"
.Text = "No"
.Location = New Drawing.Point(88, 28)
.Size = New System.Drawing.Size(39, 17)
End With
.Controls.Add(newpnlFill2)
With newpnlFill2
.Location = New System.Drawing.Point(2, 60)
.Size = New System.Drawing.Size(164, 68)
.BorderStyle = BorderStyle.FixedSingle
.Visible = False
End With
End With
End With
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
bEvent = CType(sender, RadioButton).Name
End If
End Sub
End Class
I have since figured out a solution to my delima, using your suggestions as a starting point.
I added a few varribles:
Dim rb as Control
Dim bEvent as String
Dim booFillY as Boolean
Dim booFillN as Boolean
I also added the TabControl
TabControl1.TabPages.Add(newTab)
I also made these changes :
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
rb = sender
bEvent = CType(sender, RadioButton).Name
If bEvent = "FillY" Then
Dim newpnlFill2 As Panel = rb.Parent.Controls(3)
newpnlFill2.Visible = True
End If
If bEvent = "FillN" Then
Dim newpnlFill2 As Panel = rb.Parent.Controls(3)
newpnlFill2.Visible = False
End If
End If
End Sub
Now I can make the nested panel(newpnlFill2) visible or not visible by cicking the Yes or No radiobuttons on any of the tab pages created.
thanks for your help. I doubt I would have ever gotten there on my own.
Might not be quite what you were looking for, but should be helpful get you where you need to go.
When I create an application, I like to build a list of all the controls for a given page in the load event so I can access them at any point. This is helpful because WinForms can be very picky about showing you child controls within a tabpage or groupbox, etc.
'Declare this variable within the class for your form (whatever)
Public arrControlsRecursive As New List(Of Control)
'method to recursively check all controls and build to array
Private Sub BuildControlsArrayRecursive(ByVal InControl As Control)
For Each con As Control In InControl.Controls
If con.Controls.Count > 0 Then
BuildControlsArrayRecursive(con)
End If
If TypeOf con Is Control Then
arrControlsRecursive.Add(con)
End If
Next
End Sub
'Call from MyBase.Load Event
BuildControlsArrayRecursive(Form1)
You can also just assemble a list of all tabs, for example, by changing the If statement to Is TypeOf con Is TabPage
Now you can loop through this collection or query it with LINQ. Find a single control by calling the first or single method. Cast to the type you want and do anything to any control anywhere within your form.
I don't really understand what you want to access, you first talk about
changing the nested panel's property on any page other than page one
So I assume you want to access to the other tabs, then, you talk about:
I can't seem to find a way to make the panel visible
Anyway, here's the two solutions:
Access other panels:
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
bEvent = CType(sender, RadioButton).Name '**Where is "bEvent" declared??**
Dim newpnlFill2 as Panel = bEvent.Parent.Controls(3), Panel)
newpnlFill2.Visible = bEvent.Checked
End If
End Sub
You access to Parent that will be newpnlFill, then access to Controls(3) that it should be newpnlFill2.
Access other tabs:
Private Sub CheckforCheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs)
If TypeOf sender Is RadioButton Then
bEvent = CType(sender, RadioButton).Name '**Where is "bEvent" declared??**
Dim TabControl as TabControl = bEvent.Parent.Parent.Parent, TabControl)
'Then, you can access all of the other tabs with:
'TabControl.TabPages(n)
End If
End Sub
This assume that somewhere you add your newTab to a TabControl.
I see that you never add newTab to any TabControl, so you'll never see it..
The first Parent will be newpnlFill, the second one will reference to newTab and the last one is the TabControl that hold the Tab.
Anyway, it's something really gross, cause it assumes that your Tab is always created in this manner. For example, if you will add another panel before newpnlFill, it will not be the 4th Control in the Panel anymore, so you need to change you access code.
My advice is to create your own UserControl that inherit from TabPage, in this way you can create private variables that will always reference to the Panels you want to change. Moreover, the btnAddPrgm1_Click event will be much more clear, moving the build of the page in your class constructor.
Something like:
Private Sub btnAddPrgm1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnAddPrgm1.Click
Dim newTab As New MyTabPage()
pageindx = (TabControl1.TabPages.Count + 1)
newTab.Text = "Step " & pageindx
TabControl1.TabPages.Add(newTab)
End Sub

ContextMenuStrip event handler at runtime

I create Context menu at runtime depends of text in selected cell of datagridview.
Like this:
With ContextMenuStrip1
.Items.Clear()
Dim Str As String = DataGridView1.Item(1, DataGridView1.CurrentRow.Index).Value
Dim strArr() As String = Str.Split(" ")
For count As Integer = 0 To strArr.Length - 1
If strArr(count).Length > 1 Then
.Items.Add(strArr(count))
End If
Next
.Items.Add("-")
.Items.Add("Common operation ...")
.Items.Add("Second common operation ...")
AddHandler .Click, AddressOf cMenu_Click
.Show(New Point(Cursor.Position.X, Cursor.Position.Y))
End With
etc...
Then I add event handler like this:
Private Sub cMenu_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim mytext As String
mytext = (CType(sender, ContextMenuStrip).Text)
Debug.Print(mytext)
'after all...
RemoveHandler ContextMenuStrip1.Click, AddressOf cMenu_Click
End Sub
And as vbnet beginner with this code I can't get text of fired menu item in event handler.
So please help to get it.
Each menu item needs the handler.
Try it this way (updated with adding a shortcut key):
For count As Integer = 0 To strArr.Length - 1
If strArr(count).Length > 1 Then
Dim newMenu As New ToolStripMenuItem(strArr(count), _
Nothing, AddressOf cMenu_Click)
newMenu.ShortcutKeys = Keys.Control Or Keys.C
.Items.Add(newMenu)
End If
Next
Your click method should be changed to handle a ToolStripMenuItem instead:
Private Sub cMenu_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim mytext As String
mytext = DirectCast(sender, ToolStripMenuItem).Text
Debug.Print(mytext)
End Sub
Add a handler (pointing to the same method) for the Click Event of all of the child Items of your ContextMenuStrip. Then in your method cast it as a ToolStripMenuItem or MenuItem class (whatever you're using) to find the text of the clicked item.